Object.create()
我们常见的创建对象的方式有两种,一种是通过new Object(),一种是通过对象字面量的形式。而这里我们还会再介绍的另外一种方式 —— Object.create()
Object.create(null)
let empty_obj = {};
let better_empty_obj = Object.create(null);
console.log(empty_obj); // {} 有__proto__属性
console.log(better_empty_obj); // {} 无__proto__属性
通过 Object.create(null) 创建的对象没有 Object.prototype 这个委托,所以比 {} 更空
创建关联,制造委托(对象与对象之间)
Object.create()是一个大英雄,并不只是用来让我们生成一个更空的对象(哈哈)
let foo = {
tell() {
console.log('tell me something');
}
};
let bar = Object.create(foo);
bar.tell(); // tell me something
创建一个空对象bar,并将这个对象的[[Prototype]]属性关联到foo对象,这样做和通过new一个构造函数都可以创建一个新对象,并使其[[Prototype]]属性关联到另一个对象,但是通过Object.create()的方式不会让新对象存在.constructor这样的引用(具体参看:关于constructor属性)
更多参数
let foo = {
a: 'aaa'
};
let bar = Object.create(foo, {
b: {
enumerable: false,
writable: true,
configurable: true,
value: 'bbb'
},
c: {
enumerable: true,
writable: false,
configurable: true,
value: 'bbb'
}
});
bar.hasOwnProperty('a') // false
bar.hasOwnProperty('b') // true
bar.hasOwnProperty('c') // true
bar.a // aaa
bar.b // bbb
bar.c // ccc
Object.create()的第二个参数可以为新对象增加新的属性(并可以通过属性描述符配置)
“类”与“委托”的对比
这里的类并不是说JS中有类的概念,只是模拟出这个感觉
第一段“类”代码
function Foo(who) {
this.me = who;
}
Foo.prototype.identify = function() {
return "I am " + this.me;
}
function Bar(who) {
Foo.call(this, who);
}
// 创建委托关系
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.speak = function() {
console.log('Hello, ' + this.identify());
}
var b1 = new Bar("b1");
var b2 = new Bar("b2");
b1.speak(); // Hello, I am b1
b2.speak(); // Hello, I am b2
console.log(b1.constructor);
console.log(b2.constructor);
// ƒ Foo(who) {
// this.me = who;
// }
console.log(Bar.prototype.constructor === b1.constructor); // true
第二段“委托”代码
let Foo = {
init: function(who) {
this.me = who;
},
identify: function () {
return "I am " + this.me;
}
};
let Bar = Object.create(Foo);
Bar.speak = function() {
console.log("Hello, " + this.identify());
}
var b1 = Object.create(Bar);
var b2 = Object.create(Bar);
b1.init('b1');
b2.init('b2');
b1.speak(); // Hello, I am b1
b2.speak(); // Hello, I am b2
通过比较我们可以清晰的看出关联风格(“委托”)更加的简洁,因为代码只关注一件事,对象之间的关联
关于constructor属性
对于==新==改动[[Prototype]]属性的原型对象来说,其constructor属性才会依据委托关系而改动!
先来分析一段代码
function Person() {
}
Person.prototype = {
name: 'new obj'
};
let per = new Person();
console.log(per.constructor === Person); // ?
console.log(per.constructor === Object); // ?
这里的打印的值应该是什么?
我们先来看看下面这段代码
function Person() {
}
let per = new Person();
console.log(per.constructor === Person); // true
通过图解:
首先我们要明确的是,per的constructor属性是委托于(来自于)Person.Prototype对象
所以最上面的代码我们可以拆解为:
function Person() {
}
Person.prototype = new Object();
Person.prototype.name = 'new obj';
let per = new Person();
console.log(per.constructor === Person); // false
console.log(per.constructor === Object); // true
console.log(Person.prototype.constructor === Object); // true
通过图解:
所以per对象在获取constructor属性时,会沿着原型链一直往上寻找委托的constructor属性,直到此图中的Object.Prototype对象,所以打印结果如上面的代码
所以第一段代码中结果为:
console.log(per.constructor === Person); // false
console.log(per.constructor === Object); // true