Object.create()
是 E5
的一个新特性,其实可以理解为继承一个对象,官方的定义为 Object.create()
方法可以创建一个拥有指定原型和若干个指定属性的对象
它的基本语法为
1 | Object.create(proto, [ propertiesObject ]) |
参数
proto
,一个对象,作为新创建对象的原型propertiesObject
,可选,该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符
需要注意的是,该参数对象不能是
undefined
,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的
下面我们就来看看如何使用 Object.create()
当中的 propertyObject
参数
参数
1 | // 创建一个原型为 null 的空对象 |
另外一个示例
1 | function Constructor() { } |
不过有几个需要注意的地方,第一种情况
1 | var a = { x: 1 } |
第二种情况,注意区分 __proto__
和 prototype
1 | // 如果用的是 |
实例
一般来说,Object.create()
的使用还是在继承当中使用较多,下面我们就来看一个使用它来实现类式继承的具体实现
1 | function Foo(name) { |
这段代码的核心部分就是语句 Bar.prototype = Object.create(Foo.prototype)
,调用 Object.create()
会凭空创建一个新对象并把新对象内部的 [[Prototype]]
关联到你指定的对象(本例中是 Foo.prototype
),换句话说,这条语句的意思是,创建一个新的 Bar.prototype
对象并把它关联到 Foo.prototype
声明函数 Bar()
的时候,和其他函数一样,Bar
会有一个 .prototype
关联到默认的对象,但是这个对象并不是我们想要的 Foo.prototype
,因此我们创建了一个新对象并把它关联到我们希望的对象上,直接把原始的关联对象抛弃掉,注意下面这两种方式是常见的错误做法,实际上它们都存在一些问题
1 | // 和你想要的机制不一样 |
第一种情况的 Bar.prototype = Foo.prototype
并不会创建一个关联到 Bar.prototype
的新对象,它只是让 Bar.prototype
直接引用 Foo.prototype
对象,因此当你执行类似 Bar.prototype.myLabel = ...
的赋值语句时会直接修改 Foo.prototype
对象本身
而第二种情况的 Bar.prototype = new Foo()
的确会创建一个关联到 Bar.prototype
的新对象,但是它使用了 new
操作符来进行调用,如果函数 Foo
有一些副作用(比如写日志、修改状态、注册到其他对象、给 this
添加数据属性,等等)的话,就会影响到 Bar()
的后代
因此,要创建一个合适的关联对象,我们必须使用 Object.create()
而不是使用具有副作用的 new
操作,这样做唯一的缺点就是需要创建一个新对象然后把旧对象抛弃掉,不能直接修改已有的默认对象
ES6 中的 Object.setPrototypeOf()
如果能有一个标准并且可靠的方法来修改对象的 [[Prototype]]
关联就好了,在 ES6
之前,我们只能通过设置 .__proto__
属性来实现,但是这个方法并不是标准并且无法兼容所有浏览器,ES6
添加了辅助函数 Object.setPrototypeOf()
,可以用标准并且可靠的方法来修改关联,我们来对比一下两种把 Bar.prototype
关联到 Foo.prototype
的方法
1 | // ES6 之前需要抛弃默认的 Bar.prototype |
如果忽略掉 Object.create()
方法带来的轻微性能损失(抛弃的对象需要进行垃圾回收),它实际上比 ES6
及其之后的方法更短而且可读性更高,不过无论如何,这是两种完全不同的语法
最后我们再来看一种特殊的使用方式,它就是 Object.create(null)
Object.create(null)
通过 Object.create(null)
创建出来的对象,就没有 Object.prototype
上的一些方法(需要注意与 {}
进行区分),我们可以在控制台进行简单的测试
1 | {} // {} __proto__: Object |
所以并不是所有的对象都继承有 Object.prototype
上的一些方法,看下面代码
1 | var obj = Object.create(null) |
Object.create(null)
会创建一个拥有空(或者说null
)[[Prototype]]
链接的对象,这个对象无法进行委托- 由于这个对象没有原型链,所以
instanceof
操作符无法进行判断,因此总是会返回false
这些特殊的空 [[Prototype]]
对象通常被称作字典,它们完全不会受到原型链的干扰,因此非常适合用来存储数据,另外一个需要注意的地方,那就是并不是所有的函数对象都会有 prototype
属性,代码如下
1 | function abc() {} |