一些概念
prototype
是構造函數的指針,指向原型對象。講述的是構造函數和原型對象之間的關系。
__proto__
是實例對象的指針,也指向原型對象,講述的是實例對象和原型對象之間的關系。
因為原型對象也是對象,所以原型對象也有__proto__
,指向的是這個原型對象的原型對象,JS實現繼承的方式就是根據__proto__
指針,在一個對象上查找一個property,會依次在自身對象、原型對象、原型對象的原型對象(就是原型鏈)。
所以在js中實現繼承的關鍵就是使得一個原型對象的__proto__
指針指向某一個(原型)對象。
最后在說一下constructor
指針。
constructor
是原型對象的的指針,指向構造函數。講述的是原型對象和構造函數之間的關系。所以和prototype
是互逆的一對指針。
《《JavaScript高級程序設計》》第三版講解原型和繼承對于有些基礎的人有些拖沓。其中第六章第三節講解繼承的時候也告訴了在JavaScript實現繼承的業界默認方式就是借用構造函數和原型繼承。
其背后的思路就是使用原型鏈實現對原型屬性和方法的繼承,通過借用構造函數來實現對實例屬性的繼承。
上面這句話總結的很好很全面了。
所以繼承包括兩個方面,實例屬性和原型屬性的繼承。
繼承實例屬性最常用的一種方法是上面說到的借用構造函數。
function ParentClass() { // 父類構造函數
}
function ChildrenClass(xx,yy,zz) { // 子類構造函數
ParentClass.call(this,xx,yy);
this.zz = zz;
}
繼承原型屬性的方法就有很多種了。
Object.create
function inherit(C,P) {
C.prototype = Object.create(P.prototype)
}
inherit(ChildrenClass,ParentClass)
最早老道提出來這種方法叫原型式繼承。實現了一個create方法,只不過ES5在語法層面實現了create方法,形成了上面的方法。
function create(o) {
var F = new Function();
F.prototype = o;
return new F();
}
function inherit(C,P) {
C.prototype = create(P.prototype)
}
inherit(ChildrenClass,ParentClass)
原型式繼承之二
function inherit(C,P) {
var F = new Function(); // 臨時構造函數
F.prototype = P.prototype;
C.super = P; // 使得子類能夠獲得對父類的引用
C.prototype = new F(); // 使得子類的原型對象__proto__指向父類的原型對象,從而實現繼承原型方法
C.prototype.constructor = C; // 使得子類的constructor指針重新指向子類的構造函數
}
inherit(ChildrenClass,ParentClass)
setPrototypeOf
const inherit = function(ctor, superCtor) {
if (ctor === undefined || ctor === null)
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'ctor', 'function');
if (superCtor === undefined || superCtor === null)
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'superCtor', 'function');
if (superCtor.prototype === undefined) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'superCtor.prototype',
'function');
}
ctor.super_ = superCtor;
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
};
上面一段代碼來自node的util模塊的inherits方法。使用了ES6的Object.setPrototypeOf.
其實都ES6了,為什么不直接使用ES6的extends關鍵字。
ES6
ES6的到來,為JS實現了語言層面的class。其實也只是上面內容的一個語法糖。即使在各種環境都支持ES6和class的情況下,熟悉和了解JS中繼承是如何實現的也是很有必要的。
詳細的ES6 class講解可以參考 阮一峰的ES6教程;