`Object.create()`分析及實現

[toc]javascript

Object.create()方法的做用:建立一個新對象,使用現有的對象來提供新建立的對象的__proto__(會返回一個新對象,帶着指定的原型對象和屬性)。html

分析

默認狀況下,js中對象的隱式原型__proto__指向其構造函數的顯示原型prototype(這裏的指向能夠理解爲屬性與值的關係)java

// 字面量建立對象
let obj1 = {}
obj1.__proto__ === Object.prototype; // true

// 內置構造函數建立對象。等價於new Object();
let obj2 = Object();
obj2.__proto__ === Object.prototype; // true

// 自定義構造函數建立實例對象
let Ctr = function(){};
let obj3 = new Ctr();
obj3.__proto__ === Ctr.prototype; // true
複製代碼

而通過Object.create()方法建立的對象能夠指定其隱式原型爲一個函數或者對象。node

// 首先自定義一個構造函數並初始化一個實例對象。
function Base(){
    this.name = 'cuixiaodao'
}
Base.prototype.say = function (){
    console.log(`1:`,1);
}

var base = new Base();


// 建立新對象,指定其隱式原型爲Base
var o1 = Object.create(Base);
o1.__proto__ === Base; // true

// 建立新對象,指定其隱式原型爲base
var o2 = Object.create(base);
o2.__proto__ === base; // true
複製代碼

如圖:segmentfault

能夠看出,Object.create方法的主要邏輯:建立一個對象,手動設置其隱式原型__proto__屬性爲傳入的參數,讓後將這個對象返回。函數

這樣看實現過程就比較簡單了。不過Object.create()方法還能夠接受第二個參數,用來給新建立的對象添加可枚舉屬性,與Object.defineProperies方法第二個參數用法同樣。ui

實現

這裏咱們本着弄清Object.create方法主要過程的原則,暫時不考慮第二個參數,對其主要功能作簡單實現。this

function _create(paramProto){
     var isObject = (typeof paramProto === 'object') || (typeof paramProto === 'function');
     var isUndefined = typeof paramProto === 'undefined';
     if (isUndefined || !isObject){
         throw new TypeError('Object prototype may only be an Object or null: ' + paramProto)
     }
     
     function F() { }
     F.prototype = paramProto;
     return new F();
}

複製代碼

上面最後三行代碼,返回了F的實例對象,暫且稱爲f,那麼也就是f.__proto__ === F.prototype,而F.prototype = paramProto,也就作到了f.__proto__ === paramProtospa

也能夠理解爲下面的形式prototype

function _create(paramProto) {
    return {
        __proto__: paramProto
    }
}
複製代碼

嘮叨一下

Object.create()參數爲對象和函數的區別

自定義一個構造函數而且實例化,分別用1Object.create()建立o一、o2另個對象。

function Base() {
    this.name = 'cuixiaodao'
}
Base.age = '18';
Base.prototype.say = function () {
    console.log(`1:`, 1);
}

let base = new Base();

let o1 = Object.create(Base);
var o2 = Object.create(base);
console.log(`o1:`,o1);
console.log(`o2:`,o2);
複製代碼

能夠看到o1的隱式原型是Base,而o2隱式原型是baese

o1.__proto__ === Base; // true
o2.__proto__ === base; // true
複製代碼

o2能夠訪問到base上的name屬性及base經過__proto__繼承來的say方法。

o2.name;  // cuixiaodao
o2.say(); // 1
複製代碼

可是o1都訪問不到

o1.name;  // 'Base'
o1.say;  // undefined
複製代碼

函數自帶name屬性,也就是函數名,因此這裏返回了函數的名稱Base。相似的還有length表示函數參數的個數,arguments表明函數接收的全部參數。

主要是由於原型鏈繼承是經過對象的__proto__屬性實現的:訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈。雖然o1.__proto__ === Base,但因爲say方法是定義在Base原型上的,經過o1.__proto__並訪問不到,因此是undefined。直接在Base上面定義屬性,o1是能夠訪問到的。

o1.age; // 18
複製代碼

Object.crete(null){}

Object.crete(null)會返回一個純淨的對象,不會繼承內置ObjecttoStringvalueof等方法。

參考

相關文章
相關標籤/搜索