一些概念
prototype
是構(gòu)造函數(shù)的指針泣懊,指向原型對象。講述的是構(gòu)造函數(shù)和原型對象之間的關(guān)系。
__proto__
是實(shí)例對象的指針哟楷,也指向原型對象,講述的是實(shí)例對象和原型對象之間的關(guān)系否灾。
因為原型對象也是對象卖擅,所以原型對象也有__proto__
,指向的是這個原型對象的原型對象墨技,JS實(shí)現(xiàn)繼承的方式就是根據(jù)__proto__
指針惩阶,在一個對象上查找一個property,會依次在自身對象、原型對象扣汪、原型對象的原型對象(就是原型鏈)断楷。
所以在js中實(shí)現(xiàn)繼承的關(guān)鍵就是使得一個原型對象的__proto__
指針指向某一個(原型)對象。
最后在說一下constructor
指針崭别。
constructor
是原型對象的的指針冬筒,指向構(gòu)造函數(shù)。講述的是原型對象和構(gòu)造函數(shù)之間的關(guān)系茅主。所以和prototype
是互逆的一對指針舞痰。
《《JavaScript高級程序設(shè)計》》第三版講解原型和繼承對于有些基礎(chǔ)的人有些拖沓。其中第六章第三節(jié)講解繼承的時候也告訴了在JavaScript實(shí)現(xiàn)繼承的業(yè)界默認(rèn)方式就是借用構(gòu)造函數(shù)和原型繼承诀姚。
其背后的思路就是使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承响牛,通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承。
上面這句話總結(jié)的很好很全面了赫段。
所以繼承包括兩個方面娃善,實(shí)例屬性和原型屬性的繼承。
繼承實(shí)例屬性最常用的一種方法是上面說到的借用構(gòu)造函數(shù)瑞佩。
function ParentClass() { // 父類構(gòu)造函數(shù)
}
function ChildrenClass(xx,yy,zz) { // 子類構(gòu)造函數(shù)
ParentClass.call(this,xx,yy);
this.zz = zz;
}
繼承原型屬性的方法就有很多種了聚磺。
Object.create
function inherit(C,P) {
C.prototype = Object.create(P.prototype)
}
inherit(ChildrenClass,ParentClass)
最早老道提出來這種方法叫原型式繼承。實(shí)現(xiàn)了一個create方法炬丸,只不過ES5在語法層面實(shí)現(xiàn)了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(); // 臨時構(gòu)造函數(shù)
F.prototype = P.prototype;
C.super = P; // 使得子類能夠獲得對父類的引用
C.prototype = new F(); // 使得子類的原型對象__proto__指向父類的原型對象,從而實(shí)現(xiàn)繼承原型方法
C.prototype.constructor = C; // 使得子類的constructor指針重新指向子類的構(gòu)造函數(shù)
}
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.
其實(shí)都ES6了咪啡,為什么不直接使用ES6的extends關(guān)鍵字。
ES6
ES6的到來暮屡,為JS實(shí)現(xiàn)了語言層面的class撤摸。其實(shí)也只是上面內(nèi)容的一個語法糖。即使在各種環(huán)境都支持ES6和class的情況下褒纲,熟悉和了解JS中繼承是如何實(shí)現(xiàn)的也是很有必要的准夷。
詳細(xì)的ES6 class講解可以參考 阮一峰的ES6教程;