携手创作,共同成长!这是我参与「掘金日新计划 · 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);
}
}
})