繼承是前端面試必問,說到繼承,就必須談一談原型鏈。前端
我面試的時候都會這麼回答原型鏈:js萬物皆對象,用var a={} 或 var a = new Object()或者用構造函數的形式:var a = new A()建立一個對象時,該對象不只能夠訪問它自身的屬性,還會根據__proto__屬性找到它原型鏈上的屬性,直到找到Object上面的null
。若有不貼切,望評論不足之處哈。
更詳細的參考mdn: developer.mozilla.org/zh-CN/docs/…java
對於下面的例子:面試
var A = function(){
this.name="xiaoming";
}
A.prototype.age=9;
var a = new A();
console.log(a.age); //9
複製代碼
我參考之前學習java時對實例和Class的圖畫了一個原型鏈的圖,不太好,可是畫圖不易,小女子求您一個贊哈。 bash
a.__proto__ === A.prototype; //true
A.prototype.constructor===A; //true
A.prototype.__proto__===Object.prototype; //true
Object.prototype.__proto__===null; //true
複製代碼
上方示例能夠在看完個人解釋以後再回顧一遍。實例和原型之間是經過__proto__屬性鏈接
,且是單向的,從實例指向原型
;原型和構造函數之間鏈接是雙向的,經過constructor和prototype鏈接
,具體見圖;原型鏈上的屬性是全部實例共享的
,看下面的例子:app
var A = function(){
this.name="xiaoming";
}
var a = new A();
A.prototype.age=9;
var b = new A();
console.log(a.age); //9
console.log(b.age); //9
複製代碼
a、b均可以訪問A原型鏈上的屬性age。函數
Function和Object比較特殊,他們既是對象又是函數,二者內部同時含有proto和prototype屬性,可看下面代碼:post
Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__//true
Object.prototype === Function.prototype.__proto__ // true
Function instanceof Object //true
Object instanceof Function //true
複製代碼
至此,原型鏈的知識差很少能夠理解了,後面介紹繼承的幾種方式。學習
既然能夠訪問原型鏈的全部屬性,那麼就能夠用原型鏈的原理實現繼承。原型鏈繼承用new的方式(實現A繼承B):
A.prototype=new B()
;關於new能夠看下我另外一篇文章this那些事。ui
代碼:this
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
this.nameA="A";
}
A.prototype=new B(); //原型鏈繼承:A繼承B
var a=new A();
console.log(a);
複製代碼
打印結果:
經過A構造函數new的示例a不只能夠繼承B(可訪問nameB),並且能夠繼承B原型上的屬性(nameProto),且是全部實例共享的。
好處:能夠繼承原型鏈的屬性
。
缺點:沒法實現多繼承,A繼承了B,就沒法再繼承C
。
構造繼承就是利用構造函數繼承,即改變this指向的方式(call/apply/bind)執行一次構造函數B
,具體可我另外一篇文章this那些事。廢話很少說,上例子:
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
B.call(this); //A繼承B,只舉了call的例子,apply、bind相似
this.nameA="A";
}
var a=new A();
console.log(a);
複製代碼
打印結果:
沒法繼承B原型鏈上的屬性nameProto
,不過它的好處是能夠
多繼承
,能夠經過
C.call(this)
繼承C。
好處:能夠多繼承
。
缺點:沒法繼承原型鏈上的屬性
。
組合繼承就是爲了解決原型鏈繼承沒法多繼承、構造繼承沒法繼承原型鏈上的屬性
的問題而誕生的,將兩種方式結合。
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
B.call(this); //構造繼承
this.nameA="A";
}
A.prototype=new B(); //原型鏈繼承:A繼承B
var a=new A();
console.log(a);
複製代碼
打印結果:
nameB屬性有兩個
,這樣形成了資源浪費(存儲佔用內存)。
先看下面的示例:
function objectCreate(obj){
function F(){};
F.prototype = obj;
return new F();
}
var a=objectCreate(A.prototype);
複製代碼
上個例子中,
__proto__
:A.prototype.__proto__=B.prototype
Object.create
:A.prototype=Object.create(B.prototype)