该Object.create()
方法使用现有对象作为新创建的对象的原型来创建新对象。
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
句法
Object.create(proto)
Object.create(proto, propertiesObject)
参数
proto
该对象应该是新创建对象的原型。
propertiesObject
可选的
如果未指定undefined
,则其可枚举的自身属性(即,在其自身上定义的那些属性,而不是其原型链上不可枚举的属性)的对象将指定要添加到新创建的对象的属性描述符,并带有相应的属性名称。这些属性对应于的第二个参数Object.defineProperties()
。
返回值
具有指定原型对象和属性的新对象。
例外情况
自定义和空对象
从完全自定义对象创建的新对象(尤其是从该null
对象创建的 对象,它基本上是一个没有成员的自定义对象)可能会以意外的方式运行。在调试时尤其如此,因为常见的对象属性转换/检测实用程序功能可能会生成错误或丢失信息(尤其是如果使用忽略错误的静默错误陷阱)。例如,这是两个对象:
oco = Object.create( {} ); // create a normal object
ocn = Object.create( null ); // create a "null" object
> console.log(oco) // {} -- Seems normal
> console.log(ocn) // {} -- Seems normal here too, so far
oco.p = 1; // create a simple property on normal obj
ocn.p = 0; // create a simple property on "null" obj
> console.log(oco) // {p: 1} -- Still seems normal
> console.log(ocn) // {p: 0} -- Still seems normal here too. BUT WAIT...
如上所示,到目前为止,一切似乎都很正常。但是,当尝试实际使用这些对象时,它们之间的差异很快就会变得明显:
"oco is: " + oco // shows "oco is: [object Object]"
> "ocn is: " + ocn // throws error: Cannot convert object to primitive value
仅测试许多最基本的内置函数中的几个,就可以更清楚地表明问题的严重性:
> alert(oco) // shows [object Object]
> alert(ocn) // throws error: Cannot convert object to primitive value
> oco.toString() // shows [object Object]
> ocn.toString() // throws error: ocn.toString is not a function
> oco.valueOf() // shows {}
> ocn.valueOf() // throws error: ocn.valueOf is not a function
> oco.hasOwnProperty("p") // shows "true"
> ocn.hasOwnProperty("p") // throws error: ocn.hasOwnProperty is not a function
> oco.constructor // shows "Object() { [native code] }"
> ocn.constructor // shows "undefined"
如前所述,这些差异会使调试,即使是看起来很简单的问题也迅速误入歧途。例如:
一个简单的常见调试功能:
// display top-level property name:value pairs of given object
function ShowProperties(obj){
for(var prop in obj){
console.log(prop + ": " + obj[prop] + "\n" );
}
}
结果不是那么简单:(尤其是如果静默错误捕获隐藏了错误消息)
ob={}; ob.po=oco; ob.pn=ocn; // create a compound object using the test objects from above as property values
> ShowProperties( ob ) // display top-level properties
- po: [object Object]
- Error: Cannot convert object to primitive value
Note that only first property gets shown.
(但是,如果以不同的顺序创建相同的对象-至少在某些实现中...)
ob={}; ob.pn=ocn; ob.po=oco; // create same compound object again, but create same properties in different order
> ShowProperties( ob ) // display top-level properties
- Error: Cannot convert object to primitive value
Note that neither property gets shown.
注意,这种不同的顺序可以通过诸如此处的不同的固定编码静态地产生,但是也可以通过取决于输入和/或随机变量的,在运行时实际上执行任何这种添加属性的代码分支的顺序而动态地产生。再说一次,无论添加什么顺序成员,都不能保证实际的迭代顺序。
还应注意,在通过Object.create()创建的对象上使用Object.entries()将导致返回空数组。
var obj = Object.create({ a: 1, b: 2 });
> console.log(Object.entries(obj)); // shows "[]"
一些非解决方案
对于丢失的对象方法,没有一个好的解决方案会立即显现出来。
直接从标准对象添加缺少的对象方法不起作用:
ocn = Object.create( null ); // create "null" object (same as before)
ocn.toString = Object.toString; // since new object lacks method then try assigning it directly from standard-object
> ocn.toString // shows "toString() { [native code] }" -- missing method seems to be there now
> ocn.toString == Object.toString // shows "true" -- method seems to be same as the standard object-method
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
将丢失的对象方法直接添加到新对象的“原型”中也不起作用,因为新对象没有真正的原型(这实际上是所有这些问题的原因),并且不能直接添加:
ocn = Object.create( null ); // create "null" object (same as before)
ocn.prototype.toString = Object.toString; // Error: Cannot set property 'toString' of undefined
ocn.prototype = {}; // try to create a prototype
ocn.prototype.toString = Object.toString; // since new object lacks method then try assigning it from standard-object
> ocn.toString() // error: ocn.toString is not a function
通过使用标准对象作为新对象的原型来添加缺少的对象方法,也不起作用:
ocn = Object.create( null ); // create "null" object (same as before)
Object.setPrototypeOf(ocn, Object); // set new object's prototype to the standard-object
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
一些好的解决方案
同样,直接从标准对象中添加缺少的对象方法 是行不通的。但是,直接添加 通用方法,可以做到:
ocn = Object.create( null ); // create "null" object (same as before)
ocn.toString = toString; // since new object lacks method then assign it directly from generic version
> ocn.toString() // shows "[object Object]"
> "ocn is: " + ocn // shows "ocn is: [object Object]"
ob={}; ob.pn=ocn; ob.po=oco; // create a compound object (same as before)
> ShowProperties(ob) // display top-level properties
- po: [object Object]
- pn: [object Object]
但是,将通用原型设置为新对象的原型效果更好:
ocn = Object.create( null ); // create "null" object (same as before)
Object.setPrototypeOf(ocn, Object.prototype); // set new object's prototype to the "generic" object (NOT standard-object)
(除了上面显示的所有与字符串相关的功能之外,这还添加了:)
> ocn.valueOf() // shows {}
> ocn.hasOwnProperty("x") // shows "false"
> ocn.constructor // shows "Object() { [native code] }"
// ...and all the rest of the properties and methods of Object.prototype.
如图所示,以这种方式修改的对象现在看起来非常类似于普通对象。
保鲜膜
此polyfill涵盖了主要用例,该用例正在创建一个新对象,为其选择了原型,但没有考虑第二个参数。
请注意,尽管在实际的ES5中支持null
as的设置,但由于ECMAScript版本低于5的固有限制,此polyfill无法支持它。[[Prototype]]
Object.create
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject != 'undefined') {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
}
function F() {}
F.prototype = proto;
return new F();
};
}
例子
古典继承 Object.create()
以下是如何使用Object.create()
以实现经典继承的示例。这是针对单个继承的,这是JavaScript支持的全部。
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
//If you don't set Rectangle.prototype.constructor to Rectangle,
//it will take the prototype.constructor of Shape (parent).
//To avoid that, we set the prototype.constructor to Rectangle (child).
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?', rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?', rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
如果您希望从多个对象继承,则可以使用mixins。
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// re-assign constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// do something
};
Object.assign()
将属性从OtherSuperClass原型复制到MyClass原型,使它们可用于MyClass的所有实例。 Object.assign()
是ES2015引入的,可以进行多填充。如果有必要jQuery.extend()
或 _.assign()
可以使用对较旧浏览器的支持 。
在Object.create()中使用propertiesObject参数
var o;
// create an object with null as prototype
o = Object.create(null);
o = {};
// is equivalent to:
o = Object.create(Object.prototype);
// Example where we create an object with a couple of
// sample properties. (Note that the second parameter
// maps keys to *property descriptors*.)
o = Object.create(Object.prototype, {
// foo is a regular 'value property'
foo: {
writable: true,
configurable: true,
value: 'hello'
},
// bar is a getter-and-setter (accessor) property
bar: {
configurable: false,
get: function() { return 10; },
set: function(value) {
console.log('Setting `o.bar` to', value);
}
/* with ES2015 Accessors our code can look like this
get() { return 10; },
set(value) {
console.log('Setting `o.bar` to', value);
} */
}
});
function Constructor() {}
o = new Constructor();
// is equivalent to:
o = Object.create(Constructor.prototype);
// Of course, if there is actual initialization code
// in the Constructor function,
// the Object.create() cannot reflect it
// Create a new object whose prototype is a new, empty
// object and add a single property 'p', with value 42.
o = Object.create({}, { p: { value: 42 } });
// by default properties ARE NOT writable,
// enumerable or configurable:
o.p = 24;
o.p;
// 42
o.q = 12;
for (var prop in o) {
console.log(prop);
}
// 'q'
delete o.p;
// false
// to specify an ES3 property
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
/* is not equivalent to:
This will create an object with prototype : {p: 42 }
o2 = Object.create({p: 42}) */
转自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create