『媽媽不再用擔憂之』原型鏈

AKA__proto__, prototype, constructor的愛恨情仇

總結

__proto__

  • __proto__屬性是對象所獨有的
  • 它從一個對象指向一個對象,默認指向它的原型對象(父對象),
  • 它的做用是當訪問一個對象的屬性時,若是該對象內部不存在該屬性時,那麼就會去該對象的__proto__所指向原型對象(父對象)裏找,一直找,直到找到萬物之源 Object 的__proto__屬性,此時 Object 的__proto__指向的是 null,此時就表示已經到盡頭了,確實沒有該屬性。因此__proto__屬性的終點是 null
  • 經過__proto__一層層將它們所指向的對象鏈接起來的這條鏈路就是咱們常說的原型鏈

prototype

  • prototype屬性是函數所獨有的,任何函數在建立的時候,會默認建立該函數的prototype對象
  • 它從一個函數指向一個對象,它的含義是函數的原型對象。也能夠理解爲這一類對象實例(經過該函數所建立的實例)的原型對象
  • 它的做用是存放這一類對象全部實例所共享的屬性和方法,本質是爲了節省內存

constructor

  • constructor是對象所獨有的
  • 它從一個對象指向一個函數,默認指向該對象的構造函數。每一個對象均可以找到其對應的constructor,由於建立對象的前提是須要有constructor,它多是自己擁有或繼承而來。單從constructor這個屬性來說,只有prototype對象纔有。
  • constructor易被更改,因此相對沒那麼可靠
  • Function這個對象(也是函數)比較特殊,它的構造函數就是它本身。全部函數和對象最終都是由Function構造函數得來,因此constructor屬性的終點就是Function這個函數

原型公式

// Demo
function Fun() {};
let fn = new Fun();
複製代碼

fn.__proto__ === Fun.prototype

fn.constructor === Fun

fn.__proto__.constructor === Fun

Fun.prototype.constructor === Fun

Fun.constructor === Function

Function.constructor === Function

Function.__proto__ === Function.prototype

Fun.prototype.__proto__ === Object.prototype

Function.prototype.__proto__ === Object.prototype

Object.prototype.__proto__ === null

Object.constructor === Function


__proto__constructor屬性是對象所獨有的
prototype屬性是函數所獨有的,但因爲 JS 中函數也是一種對象,因此函數也擁有__proto__constructor屬性app

圖解

__proto__屬性圖解

__proto__

prototype屬性圖解

prototype

constructor屬性圖解

constructor

constructor結合__proto__圖解

constructor和__proto__

繼承constructor

總結圖解

總結

手寫一個new試試

首先,咱們須要知道new作了哪些工做:函數

  • 建立一個新的對象obj
  • obj對象的__proto__屬性指向構造函數的原型對象prototype
  • 執行構造函數,而且傳遞參數,改變this的指向,指向新生成的對象obj
  • 若是構造函數自己有返回值,且這個返回值是對象類型,則return這個返回值;不然返回新對象obj(根據規範,返回 null 和 undefined,依然返回obj)
// 寫法1(推薦)
// 若是第一個參數不是函數,則拋出異常,由於默認只有函數纔有prototype對象
// 用 Object.create()來建立帶有你想要的[[Prototype]]的新對象。
// 執行構造函數,而且傳遞參數,將this指向obj
// 若是構造函數自己有返回值,且這個返回值是對象類型,則return這個返回值;不然返回新對象obj
function _new(Ctor, ...arg) {
    if(typeof Ctor !== 'function') {
        throw `the first param must be a function` 
    }
    let obj = Object.create(Ctor.prototype);
    let ret = Ctor.apply(obj, arg);
    return ret instanceof Object ? ret : obj;
}


// 定義構造函數
function Person(name = 'sakura') {
    this.name = name;
    console.log(this.name);
}

let person1 = _new(Person, 'eril'); // eril
let person2 = new Person('eril'); // eril
複製代碼
// 其它寫法
function _new2() {
    let obj = Object.create(null); //建立一個純的空對象
    let Constructor = [].shift.call(arguments); //將參數列表中的第一個參數截取出來做爲構造函數,執行完後參數列表長度-1
    Object.setPrototypeOf(obj, Constructor.prototype); // 更值得推薦的設置對象原型的方法,執行完後,繼承關係成立
    let rt = Constructor.apply(obj, arguments); //執行構造函數,並將this指向obj
    return typeof rt === 'object' ? rt : obj; //判斷構造函數返回值是否爲對象,是則返回構造函數返回值,不然返回新建立的對象obj
    
}

function _new3(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype; //已通過時而且不推薦的修改原型對象的方法
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); //先截取掉第一個參數(構造函數)
    // 根據規範,返回 null 和 undefined,依然返回obj
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}
複製代碼

感謝

幫你完全搞懂 JS 中的 prototype、__proto__與 constructor(圖解)post

用本身的方式(圖)理解 constructor、prototype、__proto__和原型鏈ui

相關文章
相關標籤/搜索