小議JS原型鏈、繼承

繼承是前端面試必問,說到繼承,就必須談一談原型鏈。前端

原型鏈

我面試的時候都會這麼回答原型鏈: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,圓形表明原型,三角形表明構造函數。由圖可知:

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繼承B, 經過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);
複製代碼

打印結果:

根據a的打印結果,咱們看到nameB和nameA是同一層級,雖然實現了A繼承B,可是經過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);
複製代碼

打印結果:

觀察a的打印結果,彷佛真的解決了上述兩個問題,它也引入了一個新的問題: nameB屬性有兩個,這樣形成了資源浪費(存儲佔用內存)。

原型式繼承

先看下面的示例:

function objectCreate(obj){
  function F(){};
  F.prototype = obj;
  return new F();
}
var a=objectCreate(A.prototype);
複製代碼

上個例子中,

  1. 使用__proto__A.prototype.__proto__=B.prototype
  2. 使用Object.createA.prototype=Object.create(B.prototype)
相關文章
相關標籤/搜索