关于Object.create的那丶事
语法
Object.create(proto, [propertiesObject])
参数
proto
新创建对象的原型对象
propertiesObject
可选。设置要添加到新创建对象上的属性。propertiesObject的属性名是新增属性的名字,属性值是新增属性的描述符,对应Object.defineProperties()的第二个参数
例子
var root = {rootProp: 'foo'}
var obj = Object.create(root, {
objProp: {
configurable: true,
enumerable: true,
wriable: true,
value: 'bar'}
}
)
Object.create的主要用途是实现继承
function Point (components) {
console.log('Point constructor called')
this.components = components
}
Point.prototype.getDimension = function () {
return this.components.length
}
Point.prototype.getLength = function () {
let sum = 0, components = this.components
for (let i = 0; i < components.length; i++) {
sum += components[i] ** 2
}
return Math.sqrt(sum)
}
function Point2D (x, y) {
Point.call(this, [x, y])
}
Point2D.prototype = new Point()
Point2D.prototype.getXY = function () {
let {components} = this
return {
x: components[0],
y: components[1]
}
}
var p = new Point2D(3, 4)
console.log(p, p.getLength(), p instanceof Point)
// Point constructor called
// Point constructor called
// Point2D {components: Array(2)}components: (2) [3, 4]__proto__: Point 5 true
"Point constructor called"被打印了2次说明构造函数Point被调用了2次,原因是第22行代码使用了new实现继承,而new操作符会调用构造函数,导致额外的构造函数调用
解决方法是使用Object.create来实现继承
function Point (components) {
console.log('Point constructor called')
this.components = components
}
Point.prototype.getDimension = function () {
return this.components.length
}
Point.prototype.getLength = function () {
let sum = 0, components = this.components
for (let i = 0; i < components.length; i++) {
sum += components[i] ** 2
}
return Math.sqrt(sum)
}
function Point2D (x, y) {
Point.call(this, [x, y])
}
// Point2D.prototype = new Point()
Point2D.prototype = Object.create(Point.prototype)
Point2D.prototype.getXY = function () {
let {components} = this
return {
x: components[0],
y: components[1]
}
}
var p = new Point2D(3, 4)
console.log(p, p.getLength(), p instanceof Point)
// Point constructor called
// Point2D {components: Array(2)} 5 true
polyfill
自己动手实现一个Object.create:
function clone (o) {
function F(){}
F.prototype = o
return new F()
}
Object.setPrototypeOf
Object.setPrototypeOf可以替代Object.create
但是由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。这不仅仅限于 obj.proto = ... 语句上的时间花费,而且可能会延伸到可以访问[[Prototype]]被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]。使用 Object.create()来创建带有你想要的[[Prototype]]的新对象。
// Point2D.prototype = Object.create(Point.prototype)
Object.setPrototypeOf(Point2D.prototype, Point.prototype)
如何创建没有原型的对象
通过设置构造函数的prototype实现原型继承的时候,除了根对象Object.prototype,任何对象都有一个内部属性[[Prototype]]
那么我们如何创建没有原型的对象呢?通过Object.create(null)就可以实现。
let obj = Object.create(null) // obj没有原型对象