完全搞懂js原型與原型鏈

 

原生的原型鏈html

function fn(){},fn 爲 Function的一個實例,原型鏈爲 null -> Object -> Function -> fn:函數

 fn.__proto__ === Function.prototypethis

Function.prototype.__proto === Object.prototypees5

Object.prototype.__proto__ === nullprototype

 

原型鏈經過原型(prototype)連接,prototype 就是一個 只有(構造)函數纔有的特殊的實例,包含了constructor指針(和__proto__,全部實例都有指針

以後new出來的普通實例(非prototype)都包含這個prototype(引用名爲__proto__):console.log(sonA)   //  {/* 其餘屬性 */ __proto__: Son.prototype}xml

 

舉例來講,對於函數實例,全部函數實例.__proto__ === Function.prototype (經過這個能拿到Function上的通用屬性,如bind函數)htm

甚至Function.__proto__ === Function.prototype, Object.__proto__ === Function.prototype 對象

Function instanceof Object // true  Function.__proto__.__proto__ === Object.prototype (優先是更具體的函數,而後才做爲更抽象的對象,對象永遠在原型鏈較上層)blog

Object instanceof Function // true  Object.__proto__ = Function.prototype

 

原型鏈繼承: 

未繼承時

console.log(Son.prototype) // {  constructor: Son, __proto__: Object.prototype }

 

原型鏈繼承了Father時,

console.log(Son.prototype)  // { __proto__: Father.prototype }父類的一個實例, 經過new Father 或者 Object.create(Father.prototype)生成

須要手動賦予constructor:

Object.defineProperty(Son.prototype, "constructor", {

  value: Son,

  writable: false

});

 

console.log(Object.prototype)

__proto__: null }

原型鏈繼承,本質就是修改函數的prototype,使其從指向Function變爲指向父類;父類的對象恰好符合這個特性

 

 

鞏固一下上面的知識點,請判斷如下比較是否爲真:

Object.prototype.__proto__ === null

 

Object.__proto__ === Function.prototype

Function.__proto__ === Function.prototype 

fn.__proto___ === Function.prototype

 

Object.prototype === Function.prototype.__proto__

Object.prototype === fn.prototype.__proto__ 

 

Object.prototype.isPrototypeOf(fn) 

Object.prototype.isPrototypeOf(fn.prototype)

Function.prototype === fn.__proto__; 

Function.prototype.isPrototypeOf(fn) 

Function.prototype.isPrototypeOf(fn.prototype) // false

答案:以上判斷若未特殊說明,結果都爲真

 

談一下new

var foo = new Foo();
實際是這樣調用的
 
    var this = new Object(); //Note: this no longer point to global variable if called with new
    this.__proto__ = Foo.prototype;
    Foo.call(this);
    return this;
  或者
 var this = Object.create(Foo.prototype);
   Foo.call(this);
 return this;
 
其中object.create(p)的polyfill以下:
     function f(){}
     f.prototype = p;
     return new f();

 

 

自定義原型鏈:基於原型鏈的繼承

Father() {} -> Son() {]

繼承的本質是,如何使Son的實例能訪問到Father定義的屬性

經過原型鏈,咱們將Son的原型指向Father

Son原型鏈如今是指向的Function,Son.__proto__ === Function.prototype

咱們但願Son.__proto__ === Father.prototype

按es5以前的規範,咱們不能直接訪問__proto__,那麼只能修改prototype (ES5中用Object.getPrototypeOf函數得到一個對象的[[prototype]]。ES6中,使用Object.setPrototypeOf能夠直接修改一個對象的[[prototype]])

咱們知道 Father的實例的__proto__指向Father.prototype,那麼Son.prototype等於Father的實例就能夠

Son.prototype = new Father()  或者

Son.prototype = Object.create(Father.prototype)

這裏有一個問題,prototype的constructor屬性應該指向構造函數,而這種寫法的constructor經過原型鏈會找到Father,因此須要定義一下constructor

Object.defineProperty(Son.prototype, "constructor", {

  value: Son,

  writable: false

});

 

順便了解一下原型鏈查找順序

優先查找對象的,對象上沒有則經過__proto__找prototype,還沒找到,就找prototype.__proto__, 也就是父類的prototype

好比

對象objA查找hasOwnProperty 

objA -> Son.prototype -> Object.prototype -> Object.prototype.hasOwnProperty

函數funcA查找bind

funcA -> Function.prototype -> Function.prototype.bind

 

 

 

PS:對於 const fn = () => {} 這樣的箭頭函數,不屬於原型鏈的範疇 能夠參考 http://www.cnblogs.com/mengff/p/9656486.html

相關文章
相關標籤/搜索