การปรับปรุงวัตถุ ECMAScript
- 上一页 定义类或对象
- หน้าต่อไป ตัวอย่างของทฤษฎีการสืบทอด
ด้วยการใช้ ECMAScript ไม่เพียงแค่สามารถสร้างวัตถุแต่ยังสามารถเปลี่ยนพฤติกรรมของวัตถุที่มีอยู่แล้ว
属性 prototype ไม่เพียงแค่สามารถกำหนดตัวแปรและฟังก์ชันของฟังก์ชันก่อนสร้างแต่ยังสามารถเพิ่มตัวแปรและฟังก์ชันสำหรับสิ่งที่ถูกก่อสร้างท้องถิ่น
สร้างวิธี
สร้างวิธีโดยใช้วิธีที่มีอยู่
สามารถใช้ตัวแปร prototype ในการกำหนดวิธีใหม่สำหรับรายการที่มีอยู่ ให้เหมือนกับการจัดการกับรายการของตัวเอง ตัวอย่างเช่น จำแนกความรู้จักวิธี toString() ของ Number โดยส่งตัวเลข 16 ก็จะทำให้ทำการแสดงตัวเลขในระบบสิบหก หากส่งตัวเลข 2 ก็จะแสดงตัวเลขในระบบสองทศนิยม พวกเราสามารถสร้างวิธีดังนี้เพื่อที่จะทำการแปลงตัวเลขโอปเปอร์เกอร์นให้เป็นตัวเลขสิบหกได้ง่ายๆ
Number.prototype.toHexString = function() { return this.toString(16); };
ในสภาพความเป็นอยู่นี้ คำกำหนด this บอกถึงตัวอย่างของ Number จึงสามารถเข้าถึงวิธีทั้งหมดของ Number ได้ทั้งหมด ด้วยโค้ดนี้ เราสามารถทำการดังนี้ได้
var iNum = 15; alert(iNum.toHexString()); // แสดง "F"
เพราะตัวเลข 15 มีตัวอักษร F ในระบบสิบหก ดังนั้น คำเตือนจะแสดง "F"
เปลี่ยนชื่อวิธีที่มีอยู่
เรายังสามารถทำให้วิธีการที่มีอยู่มีชื่อที่ง่ายต่อการเข้าใจ ตัวอย่างเช่น สามารถเพิ่มวิธี enqueue() และ dequeue() ให้กับ Array โดยเรียกวิธี push() และ shift() ที่มีอยู่ได้
Array.prototype.enqueue = function(vItem) { this.push(vItem); }; Array.prototype.dequeue = function() { return this.shift(); };
เพิ่มวิธีการที่ไม่มีความเกี่ยวข้องกับวิธีการที่มีอยู่
นอกจากนี้ ยังสามารถเพิ่มวิธีการที่ไม่มีความเกี่ยวข้องกับวิธีการที่มีอยู่ ตัวอย่างเช่น หากต้องการตรวจสอบตำแหน่งของรายการในตารางค่า ไม่มีวิธีท้องถิ่นที่สามารถทำได้ พวกเราสามารถสร้างวิธีดังนี้ได้ง่ายๆ
Array.prototype.indexOf = function (vItem) { for (var i=0; i<this.length; i++) { if (vItem == this[i]) { return i; } } return -1; }
ขั้นตอนนี้ indexOf() ใช้งานตามเดียวกับวิธีการจาก String ในลักษณะเดียวกัน ในการค้นหาแต่ละรายการในตารางค่า จนกว่าจะพบรายการที่เท่ากันกับรายการที่ส่งมา หากพบรายการที่เท่ากัน ก็จะกลับค่าตำแหน่งของรายการนั้น หากไม่พบก็กลับค่า -1 ด้วยการกำหนดนี้ เราสามารถเขียนโค้ดดังนี้ได้
var aColors = new Array("red","green","blue"); alert(aColors.indexOf("green")); // ออก "1"
เพิ่มวิธีการใหม่ให้กับชนิดท้องถิ่น
สุดท้าย ถ้าคุณต้องการที่จะเพิ่มวิธีการใหม่ให้กับทุกชนิดท้องถิ่นใน ECMAScript คุณจะต้องกำหนดมันบนรายละเอียด prototype ของชนิด Object บทก่อนหน้าเราได้พูดถึงว่า ทุกชนิดท้องถิ่นต่างก็สืบทอดมาจากชนิด Object ดังนั้น การเปลี่ยนแปลงใดๆ ที่ทำให้แก้ไขชนิด Object จะทำให้ผลลัพธ์ดังกล่าวสะท้อนให้เห็นทุกชนิดท้องถิ่น ตัวอย่างเช่น ถ้าคุณต้องการที่จะเพิ่มวิธีการที่แจ้งเตือนของค่าปัจจุบันของตัวแปร คุณสามารถใช้รหัสดังนี้:
Object.prototype.showValue = function () { alert(this.valueOf()); }; var str = "hello"; var iNum = 25; str.showValue(); // ออก "hello" iNum.showValue(); // ออก "25"
ที่นี้ ชนิด String และ Number ทั้งหมดมีวิธีการ showValue() ที่สืบทอดมาจากชนิด Object โดยเรียกวิธีการนี้บนชนิดที่มีอยู่ จะแสดง "hello" และ "25":
กำหนดวิธีการใหม่แทนวิธีการที่มีอยู่แล้ว
เช่นเดียวกับที่คุณสามารถกำหนดวิธีการใหม่ให้กับชนิดที่มีอยู่แล้ว คุณก็สามารถกำหนดวิธีการใหม่แทนวิธีการที่มีอยู่แล้ว อย่างเช่น ตามที่เราได้พูดถึงในบทก่อนหน้า ชื่อฟังก์ชันเป็นบิตที่บอกถึงฟังก์ชัน ดังนั้น คุณสามารถใช้งานง่ายๆ ในการหน้าตายับยังฟังก์ชันอื่น ถ้าคุณเปลี่ยนวิธีการท้องถิ่น อย่างเช่น toString() มันจะเกิดอะไรขึ้น?
Function.prototype.toString = function() { return "Function code hidden"; }
รหัสที่มาก่อนนี้เป็นรหัสที่ถูกต้องทั้งหมด และผลลัพธ์ที่ออกมาเป็นที่คาดหวังได้:
function sayHi() { alert("hi"); } alert(sayHi.toString()); // ออก "Function code hidden"
อาจคุณจะจำได้ว่า ในบท Function Object ที่เราได้นำเสนอไปแล้ว วิธีการ toString() ของ Function ทั่วไปจะออกมาเป็นรหัสแหล่งของฟังก์ชัน (function source code) นั้นตามปกติออกมาเป็นว่าง (hidden) หากคุณเปลี่ยนวิธีการนี้ คุณสามารถกลับคืนค่าตัวแปรอื่น (ในตัวอย่างนี้ คือ "Function code hidden") ได้ แต่วิธีการ toString() ที่หมายหน้าต้นกันของฟังก์ชันเดิมเช่นไร? มันจะถูกสูญเสียโดยโปรแกรมการทำลายหน่วยเก็บข้อมูลที่ไม่ได้ใช้งาน (garbage collector) เพราะมันถูกใช้ไปแล้ว ไม่มีวิธีที่จะกู้ฟังก์ชันเดิม ดังนั้น ก่อนที่จะเปลี่ยนวิธีการเดิม ทางการดูแลเป็นเรื่องปลอดภัยมากที่จะเก็บบิตที่บอกถึงฟังก์ชันนี้เพื่อใช้ในอนาคต บางครั้งคุณอาจเรียกวิธีการเดิมในวิธีการใหม่ของคุณ:
Function.prototype.originalToString = Function.prototype.toString; Function.prototype.toString = function() { if (this.originalToString().length > 100) { return "Function too long to display."; } else { return this.originalToString(); } };
在这段代码中,第一行代码把对当前 toString() 方法的引用保存在属性 originalToString 中。然后用定制的方法覆盖了 toString() 方法。新方法将检查该函数源代码的长度是否大于 100。如果是,就返回错误信息,说明该函数代码太长,否则调用 originalToString() 方法,返回函数的源代码。
极晚绑定(Very Late Binding)
从技术上讲,根本不存在极晚绑定。本书采用该术语描述 ECMAScript 中的一种现象,即能够在对象实例化后再定义它的方法。例如:
var o = new Object(); Object.prototype.sayHi = function () { alert("hi"); }; o.sayHi();
在大多数程序设计语言中,必须在实例化对象之前定义对象的方法。这里,方法 sayHi() 是在创建 Object 类的一个实例之后来添加进来的。在传统语言中不仅没听说过这种操作,也没听说过该方法还会自动赋予 Object 对象的实例并能立即使用(接下来的一行)。
注意:不建议使用极晚绑定方法,因为很难对其跟踪和记录。不过,还是应该了解这种可能。
- 上一页 定义类或对象
- หน้าต่อไป ตัวอย่างของทฤษฎีการสืบทอด