Object.create()---【實現寄生組合式繼承是關鍵】
Object.create()
方法建立一個新物件,使用現有的物件來提供新建立的物件的__proto__。 (請開啟瀏覽器控制檯以檢視執行結果。)
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
語法
Object.create(proto, [propertiesObject])
引數
proto
新建立物件的原型物件。
propertiesObject
可選。如果沒有指定為 undefined
,則是要新增到新建立物件的可列舉屬性(即其自身定義的屬性,而不是其原型鏈上的列舉屬性)物件的屬性描述符以及相應的屬性名稱。這些屬性對應Object.defineProperties()
的第二個引數。
返回值
一個新物件,帶著指定的原型物件和屬性。
例外
如果propertiesObject
引數不是 null
或一個物件,則丟擲一個 TypeError
異常。
例子
用 Object.create
實現類式繼承
下面的例子演示瞭如何使用Object.create()
來實現類式繼承。這是一個所有版本JavaScript都支援的單繼承。
// Shape - 父類(superclass) function Shape() { this.x = 0; this.y = 0; } // 父類的方法 Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info('Shape moved.'); }; // Rectangle - 子類(subclass) function Rectangle() { Shape.call(this); // call super constructor. } // 子類續承父類 Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle; var rect = new Rectangle(); console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle); // true console.log('Is rect an instance of Shape?', rect instanceof Shape); // true rect.move(1, 1); // Outputs, 'Shape moved.'
如果你希望能繼承到多個物件,則可以使用混入的方式。
function MyClass() { SuperClass.call(this); OtherSuperClass.call(this); } // 繼承一個類 MyClass.prototype = Object.create(SuperClass.prototype); // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype); // 重新指定constructor MyClass.prototype.constructor = MyClass; MyClass.prototype.myMethod = function() { // do a thing };
Object.assign 會把 OtherSuperClass
原型上的函式拷貝到 MyClass
原型上,使 MyClass 的所有例項都可用 OtherSuperClass 的方法。Object.assign 是在 ES2015 引入的,且可用 polyfilled。要支援舊瀏覽器的話,可用使用 jQuery.extend() 或者 _.assign()。
使用 Object.create
的 propertyObject
引數
var o;
// 建立一個原型為null的空物件
o = Object.create(null);
o = {};
// 以字面量方式建立的空物件就相當於:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo會成為所建立物件的資料屬性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar會成為所建立物件的訪問器屬性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
function Constructor(){}
o = new Constructor();
// 上面的一句就相當於:
o = Object.create(Constructor.prototype);
// 當然,如果在Constructor函式中有一些初始化程式碼,Object.create不能執行那些程式碼
// 建立一個以另一個空物件為原型,且擁有一個屬性p的物件
o = Object.create({}, { p: { value: 42 } })
// 省略了的屬性特性預設為false,所以屬性p是不可寫,不可列舉,不可配置的:
o.p = 24
o.p
//42
o.q = 12
for (var prop in o) {
console.log(prop)
}
//"q"
delete o.p
//false
//建立一個可寫的,可列舉的,可配置的屬性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
Polyfill
這個 polyfill 涵蓋了主要的應用場景,它建立一個已經選擇了原型的新物件,但沒有把第二個引數考慮在內。
請注意,儘管在 ES5 中 Object.create
支援設定為[[Prototype]]
為null
,但因為那些ECMAScript5以前版本限制,此 polyfill 無法支援該特性。
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject != 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
}