เป็นปัญหาที่ยังไม่สามารถทำให้เกิดขึ้นอีกในโค้ดแบบอื่นได้ แต่เอามาเขียนไว้ก่อนเพราะมันสร้างความปวดหัวให้กับคนเขียน Javascript มาก่อนมากมาย และไม่รู้จะเจอมันอีกทีเหมื่อไหร่ แต่ไม่สามารถเอาโค้ดต้นฉบับทั้งก้อนมาใส่ในนี้ได้ เลยขอแค่เล่าแล้วเอา stacktrace มาแปะใส่ไว้ละกัน
เหตุการณ์เริ่มต้นเกิดจาก @pitiphong_p เจอบั๊กหนึ่งที่ไม่รู็จะแก้อย่างไรเข้าเพราะใน Flash Builder Debugger บอกว่าตัวแปรต่างๆ มีค่าครบสมบุรณ์หมด แต่ error บอกไม่สามารถหาตัวแปรนั้นได้
[Fault] exception, information=TypeError: Error #1010: A term is undefined and has no properties. Fault, CheckboxListRenderer.as:21 21 data.selected = checkbox.selected (fdb) bt #0 this = [Object 32588233, class='global'].<anonymous>(event=[Object 659079665, class='flash.events::Event']) at CheckboxListRenderer.as:21 #1 EventDispatcher/dispatchEventFunction() at <null>:0 #2 this = [Object 655130785, class='mx.controls::CheckBox'].EventDispatcher/dispatchEvent(_arg1=[Object 659079665, class='flash.events::Event']) at <null>:0 #3 this = [Object 655130785, class='mx.controls::CheckBox'].UIComponent/dispatchEvent(event=[Object 659079665, class='flash.events::Event']) at UIComponent.as:9440 #4 this = [Object 655130785, class='mx.controls::CheckBox'].Button/http://www.adobe.com/2006/flex/mx/internal::setSelected(value=true, isProgrammatic=false) at Button.as:1204 #5 this = [Object 655130785, class='mx.controls::CheckBox'].Button/clickHandler(event=[Object 591642521, class='flash.events::MouseEvent']) at Button.as:2798
ตอนแรกก็พยายามคาดเดาปัญหาไปต่าง ๆ อาจเกิดจากการ bind ตัวแปรผิดที่ หรือตอนกำหนดค่าผิด แต่ถ้าอย่างนั้น ทำไมตอน debug ถึงสามารถเอาค่าออกมาดูได้หละ ลองดูโค้ดเจ้าปัญหาซักนิดก่อนละกัน
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package sample { import mx.binding.utils.BindingUtils import mx.containers.HBox import mx.controls.Alert import mx.controls.CheckBox import mx.core.IFactory import flash.events.Event public class CheckboxListRenderer extends HBox { [Bindable] public var listRenderer:IFactory public var checkbox:CheckBox = new CheckBox() private var instance:* private var checkboxChange = function(event:Event):void { if (data.hasOwnProperty('selected')) { data.selected = checkbox.selected } } public override function set data(item:Object):void { super.data = item if (item && item.hasOwnProperty('selected')) { checkbox.selected = item.selected } } protected override function createChildren():void { super.createChildren() addChild(checkbox) if (listRenderer != null) { instance = listRenderer.newInstance() addChild(instance) } trace("Checkbox list renderer created children") } protected override function commitProperties():void { super.commitProperties() horizontalScrollPolicy = "off" verticalScrollPolicy = "off" // Handle change in targeting phase for changing data selected flag. checkbox.addEventListener(Event.CHANGE, checkboxChange) BindingUtils.bindProperty(instance, "data", this, "data") } } } |
รับรองได้เลยว่าถ้าใครเอาโค้ดด้านบนไปลองเล่นดูเองจะไม่เจอปัญหาแน่นอน ซึ่งถึงวันนี้ยังไม่เข้าใจเหมือนกันว่าทำยังไงถึงจะเกิดปัญหาได้ แต่วิธีแก้นั้นง่ายมากคือ เปลี่ยนวิธีประกาศฟังก์ชั่นจาก
20 21 22 23 24 | private var checkboxChange = function(event:Event):void { if (data.hasOwnProperty('selected')) { data.selected = checkbox.selected } } |
เป็น
20 21 22 23 24 | private function checkboxChange(event:Event):void { if (data.hasOwnProperty('selected')) { data.selected = checkbox.selected } } |
ปัญหาทุกอย่างก็หายไปอย่างปริศนา ทิ้งไว้แต่เครื่องหมายคำถามว่าทำไมขอบเขตของฟังก์ชั่นถึงต่างกันเพียงแค่เปลี่ยนรูปแบบการประกาศฟังก์ชั่นเท่านั้น และไม่สามารถทดลองซ้ำกับการเขียนโค้ดแบบง่าย ๆ ได้ แต่หลังจากนี้รู้แล้วว่า จะไม่ประกาศฟังก์ชั่นในรูปแบบตัวแปรอีก เพราะไม่อยากเจอปัญหาแปลกๆ แบบนี้ให้นั่งปวดหัวหาวิธีแก้อีกแล้ว
เอ๊ะ หรือมันจะเป็น bug ของ Flash builder beta 4???
ผมสงสัยว่า แล้ว checkboxChange แบบที่บอกใช้ไม่ได้อะครับ ถ้าเรียกตรงๆ ไม่ได้เรียกผ่าน eventListener เรียกทำงานได้รึเปล่าครับ?
ได้เพราะ scope ตอนเรียกอยู่ในคลาสแล้ว