JavaScript 語言核心(15)函式 prototype 特性
在 隱藏諸多細節的建構式 中看過一個例子: function toString() { return '[' + this.name + ',' + this.age + ']'; } function Person(name, age) { this.name = name; this.age = age; this.toString = toString; } 這可以解決重複建立函式實例的問題,但在全域範圍(物件)上多了個 function Person(name, age) { this.name = name; this.age = age; this.toString = function() { return '[' + this.name + ', ' + this.age + ']'; }; }
如果你知道函式在定義時,都有個 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.toString = function() { return '[' + this.name + ', ' + this.age + ']'; }; var p1 = new Person('Justin', 35); var p2 = new Person('Momor', 32); console.log(p1.toString()); // [Justin, 35] console.log(p2.toString()); // [Momor, 32] 使用 JavaScript 在尋找特性名稱時,會先在實例上找尋有無特性,以上例而言, 如果使用 ECMAScript 5,可以透過 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.toString = function() { return '[' + this.name + ', ' + this.age + ']'; }; var p = new Person('Justin', 35); console.log(Person.prototype === Object.getPrototypeOf(p)); // true Node.js、Rhino、Nashorn 中,物件都有個「非標準」特性 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.toString = function() { return '[' + this.name + ', ' + this.age + ']'; }; var p = {}; p.__proto__ = Person.prototype; Person.call(p, 'Justin', 35); console.log(p.toString()); // [Justin,35] console.log(p instanceof Person); // true 要注意的是,只有在查找特性,而物件上不具該特性時才會使用原型,如果你對物件設定某個特性,是直接在物件上設定了特性,而不是對原型設定了特性。例如: function Some() {} Some.prototype.data = 10; var s = new Some(); console.log(s.data); // 10 s.data = 20; console.log(s.data); // 20 console.log(Some.prototype.data); // 10 在上例中可以看到,你對 你可以在任何時間點對函式的 function Some() {} var s = new Some(); console.log(s.data); // undefined Some.prototype.data = 10; console.log(s.data); // 10 先前在談建構式時有提過,每個透過 function Some() {} console.log(Some.prototype.constructor); // [Function: Some] 每個函式實例,其 例如: Object.prototype.xyz = 10; function Some() {} var s = new Some(); console.log(s.xyz); // 10 console.log(Object.getPrototypeOf(s) === Some.prototype); // true var protoOfS = Object.getPrototypeOf(s); console.log(Object.getPrototypeOf(protoOfS) === Object.prototype); // true 實例的原型物件,預設就是建構式的 你也可以使用 var arr = []; console.log(Array.prototype.isPrototypeOf(arr)); // true console.log(Function.prototype.isPrototypeOf(Array)); // true console.log(Object.prototype.isPrototypeOf(Array.prototype)); // true
如果要建立一個實例,想要令其循著某個原型鏈查找,例如,想要建立一個類似陣列的物件,但要其可循著 var arrayLike = { '0' : 10, '1' : 20, '2' : 30, length : 3 }; arrayLike.__proto__ = []; arrayLike.map(function(elem) { return elem * 10 }) .forEach(function(elem) { console.log(elem); }); 不過, Object.beget = function (o) { var F = function () {}; F.prototype = o; return new F(); }; var arrayLike = Object.beget(Array.prototype); arrayLike[0] = 10; arrayLike[1] = 20; arrayLike[2] = 30; arrayLike.length = 3; arrayLike.map(function(elem) { return elem * 10 }) .forEach(function(elem) { console.log(elem); }); ECMAScript 5 中包括了一個 var arrayLike = Object.create(Array.prototype, { '0' : {value : 10}, '1' : {value : 20}, '2' : {value : 30}, length : {value : 3} }); arrayLike.map(function(elem) { return elem * 10 }) .forEach(function(elem) { console.log(elem); });
Object.create = function(proto, props) { var ctor = function(ps) { if(ps) { Object.defineProperties( this, ps ); } }; ctor.prototype = proto; return new ctor(props); }; 因此,作為一個有趣的練習,先前有個範例使用了 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.toString = function() { return '[' + this.name + ', ' + this.age + ']'; }; var p = Object.create(Person.prototype); Person.call(p, 'Justin', 35); console.log(p.toString()); // [Justin,35] console.log(p instanceof Person); // true |