手写Object.create()

195 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

(终于有7天了呜呜呜,好难写,我还得准备秋招呢qaq~)

首先我们得明白Object.create()的作用是啥。一般情况下,我们使用这个是为了实现继承

继承就是沿着原型链找,能找到父级的原型,并能找到其父级的属性和方法。

Object.create(obj)会创建一个新对象,并将这个参数obj设为这个新对象的__proto__;所以常常用它来实现继承。

function Person(name.age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    console.log('Person');
}
function Son() {};
Son.prototype = Object.create(Person.prototype); //实现了继承
​
​
let my = new Son();
my.sayName();   //  'Person'

解:

new Son() 创建一个实例对象,该对象本身没有sayName方法,去原型链__proto__上面找,__proto__指向的是Son的prototype。本来Son.prototype是一个对象,上面啥也没有,就一个属性:constructor,还有一个__proto__指向创建这个对象的构造函数的原型----Object.prototype。但是现在这个指针被改变了,因为Son.prototype = Object.create(Person.prototype);改变了这个指针。Object.create(Person.prototype)返回了一个对象,这个对象为空,但是这个对象的__proro__指向的是Person.prototype,然后将这个对象的地址赋值给了Son.prototype.所以现在Son.prototype就指向了这个对象了。所以Son.prototype.__proto__指向了Person.prototype.所以my.__proto__.__proto__ === Son.prototype.__proto__ === Person.prototype

懂了这个原理,我们写它的底层源码就简单了:

function create(obj){
    let Fn = function(){};
    Fn.prototype = obj;
    return new Fn();
}

但其实,Object.create()是有第二个参数的,这个参数是给这个新创建的对象添加属性的,也就是说,有了第二个参数,返回的就不是空对象了。

const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});
console.log(ages.alice); // 18

所以底层应该也要对第二个参数进行处理,且第一个参数不能为null,因为为null的话,最后返回的对象的原型链指向了Object(这里很奇怪,我也没明白为啥指向了Object,不应该指向null吗??):

function create(obj,options) {
    //(注解掉) if(obj===null) return new Error('obj is null');
    let Fn = function() {};
    Fn.prototype = obj;
    let o = new Fn();
    if(options) {
        for(let [k,v] of Object.entries(options)) {
            Object.defineProperty(o,k,v);
        }
    }
    return o;
}
let o = create(null,{
  foo: {
    writable: true,
    configurable: true,
    value: 'hello'
  },
  bar: {
    configurable: false,
    get: function() { return 10; },
    set: function(value) {
      console.log('Setting `o.bar` to', value);
    }
  }
})

image-20220717102501100.png