使用“Object.create”而不是“新的”
Javascript 1.9.3 / ECMAScript 5 引入了 Object.create
,Douglas Crockford 等人已经长期倡导。如何将下面代码中的 new
替换为 Object.create
?
var UserA = function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
}
UserA.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();
(假设 MY_GLOBAL.nextId
存在)。
我能想到的最好的办法是:
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();
似乎没有任何优势,所以我想我没有得到它。我可能太新古典主义了。我应该如何使用Object.create
来创建用户“bob”?
Javascript 1.9.3 / ECMAScript 5 introduces Object.create
, which Douglas Crockford amongst others has been advocating for a long time. How do I replace new
in the code below with Object.create
?
var UserA = function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
}
UserA.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();
(Assume MY_GLOBAL.nextId
exists).
The best I can come up with is:
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();
There doesn't seem to be any advantage, so I think I'm not getting it. I'm probably being too neo-classical. How should I use Object.create
to create user 'bob'?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
仅具有一级继承,您的示例可能无法让您看到
Object.create
。此方法允许您轻松实现差异继承,其中对象可以直接从其他对象继承。
在您的
userB
示例中,我认为您的init
方法不应该是公共的,甚至不应该存在,如果您在现有对象实例上再次调用此方法,id
和name
属性将会改变。Object.create
允许您使用其第二个参数初始化对象属性,例如:如您所见,可以在
Object.create
的第二个参数上初始化属性,使用对象字面量使用与Object.defineProperties
和Object.defineProperty
方法使用的语法类似的语法。它允许您设置属性属性(
enumerable
、writable
或configurable
),这非常有用。With only one level of inheritance, your example may not let you see the real benefits of
Object.create
.This methods allows you to easily implement differential inheritance, where objects can directly inherit from other objects.
On your
userB
example, I don't think that yourinit
method should be public or even exist, if you call again this method on an existing object instance, theid
andname
properties will change.Object.create
lets you initialize object properties using its second argument, e.g.:As you can see, the properties can be initialized on the second argument of
Object.create
, with an object literal using a syntax similar to the used by theObject.defineProperties
andObject.defineProperty
methods.It lets you set the property attributes (
enumerable
,writable
, orconfigurable
), which can be really useful.使用
Object.create(...)
相对于new object
确实没有任何优势。那些提倡这种方法的人通常会陈述相当模糊的优点:“可扩展性”,或“JavaScript 更自然" 等。
但是,我还没有看到一个具体的例子来表明
Object.create
与使用new
相比有任何优势。相反,它存在已知的问题。 Sam Elsamman 描述了当存在嵌套对象和Object.create(...)
时会发生什么情况被使用:出现这种情况是因为
Object.create(...)
提倡使用数据创建新对象的做法;这里,Animal
数据成为lion
和bird
原型的一部分,并在共享时引起问题。当使用 new 时,原型继承是显式的:关于传递到
Object.create(...)
的可选属性,可以使用Object.defineProperties(...)
。There is really no advantage in using
Object.create(...)
overnew object
.Those advocating this method generally state rather ambiguous advantages: "scalability", or "more natural to JavaScript" etc.
However, I have yet to see a concrete example that shows that
Object.create
has any advantages over usingnew
. On the contrary there are known problems with it. Sam Elsamman describes what happens when there are nested objects andObject.create(...)
is used:This occurs because
Object.create(...)
advocates a practice where data is used to create new objects; here theAnimal
datum becomes part of the prototype oflion
andbird
, and causes problems as it is shared. When using new the prototypal inheritance is explicit:Regarding, the optional property attributes that are passed into
Object.create(...)
, these can be added usingObject.defineProperties(...)
.Object.create 在一些浏览器上还不是标准的,例如 IE8、Opera v11.5、Konq 4.3 都没有它。您可以为这些浏览器使用 Douglas Crockford 版本的 Object.create,但这不包括 CMS 答案中使用的第二个“初始化对象”参数。
对于跨浏览器代码,同时进行对象初始化的一种方法是自定义 Crockford 的 Object.create。这是一种方法:-
这维护了 Crockford 原型继承,并且还检查对象中的任何 init 方法,然后使用您的参数运行它,例如 new man('John','Smith')。然后您的代码将变为:-
所以 bob 继承了 sayHello 方法,现在拥有自己的属性 id=1 和 name='Bob'。当然,这些属性既可写又可枚举。这也是比 ECMA Object.create 更简单的初始化方法,特别是如果您不关心可写、可枚举和可配置属性的话。
对于没有 init 方法的初始化,可以使用以下 Crockford mod: -
这将按照定义的顺序填充 userB 自己的属性,在 userB 参数之后从左到右使用 Object.gen 参数。它使用 for(prop in o) 循环,因此根据 ECMA 标准,不能保证属性枚举的顺序与属性定义的顺序相同。然而,在 (4) 个主要浏览器上测试的几个代码示例表明,只要使用了 hasOwnProperty 过滤器,它们是相同的,有时即使不使用也是如此。
我想说比 Object.build 更简单,因为 userB 不需要 init 方法。另外,userB 并不是专门的构造函数,而是看起来像普通的单例对象。因此,使用此方法,您可以从普通的普通对象构造和初始化。
Object.create is not yet standard on several browsers, for example IE8, Opera v11.5, Konq 4.3 do not have it. You can use Douglas Crockford's version of Object.create for those browsers but this doesn't include the second 'initialisation object' parameter used in CMS's answer.
For cross browser code one way to get object initialisation in the meantime is to customise Crockford's Object.create. Here is one method:-
This maintains Crockford prototypal inheritance, and also checks for any init method in the object, then runs it with your parameter(s), like say new man('John','Smith'). Your code then becomes:-
So bob inherits the sayHello method and now has own properties id=1 and name='Bob'. These properties are both writable and enumerable of course. This is also a much simpler way to initialise than for ECMA Object.create especially if you aren't concerned about the writable, enumerable and configurable attributes.
For initialisation without an init method the following Crockford mod could be used:-
This fills the userB own properties, in the order they are defined, using the Object.gen parameters from left to right after the userB parameter. It uses the for(prop in o) loop so, by ECMA standards, the order of property enumeration cannot be guaranteed the same as the order of property definition. However, several code examples tested on (4) major browsers show they are the same, provided the hasOwnProperty filter is used, and sometimes even if not.
Somewhat simpler I would say than Object.build since userB does not need an init method. Also userB is not specifically a constructor but looks like a normal singleton object. So with this method you can construct and initialise from normal plain objects.
TL;DR:
new Computer()
将调用构造函数Computer(){}
一次,而Object.create(Computer.prototype)
代码> 不会。所有的优点都是基于这一点。
关于性能的旁注:像
new Computer()
这样的构造函数调用经过引擎的大力优化,因此它可能比Object.create
更快。TL;DR:
new Computer()
will invoke the constructor functionComputer(){}
for one time, whileObject.create(Computer.prototype)
won't.All the advantages are based on this point.
Sidenote about performance: Constructor invoking like
new Computer()
is heavily optimized by the engine, so it may be even faster thanObject.create
.您可以使
init
方法返回this
,然后将调用链接在一起,如下所示:You could make the
init
method returnthis
, and then chain the calls together, like this:Object.create 的另一种可能用法是以廉价而有效的方式克隆不可变对象。
注释:上面的代码片段创建了源对象的克隆(也称为引用,如 cObj = aObj)。它优于复制属性方法(请参阅1),因为它不复制对象成员属性。相反,它创建另一个目标对象,其原型设置在源对象上。此外,当在目标对象上修改属性时,它们是“动态”创建的,掩盖了原型(src)的属性。这构成了克隆不可变对象的快速有效方法。
这里需要注意的是,这适用于创建后不应修改的源对象(不可变)。如果源对象在创建后被修改,则克隆的所有未屏蔽属性也将被修改。
在这里小提琴(http://jsfiddle.net/y5b5q/1/)(需要Object.create能力浏览器)。
Another possible usage of Object.create is to clone immutable objects in a cheap and effective way.
Notes: The above snippet creates a clone of an source object (aka not a reference, as in cObj = aObj). It benefits over the copy-properties method (see 1), in that it does not copy object member properties. Rather it creates another -destination- object with it's prototype set on the source object. Moreover when properties are modified on the dest object, they are created "on the fly", masking the prototype's (src's) properties.This constitutes a fast an effective way of cloning immutable objects.
The caveat here is that this applies to source objects that should not be modified after creation (immutable). If the source object is modified after creation, all the clone's unmasked properties will be modified, too.
Fiddle here(http://jsfiddle.net/y5b5q/1/) (needs Object.create capable browser).
我认为问题的要点是理解 new 和 Object.create 方法之间的区别。根据此答案和此视频
new
关键字执行以下操作:创建新对象。
将新对象链接到构造函数(
prototype
)。使
this
变量指向新对象。使用新对象执行构造函数并隐式执行
return this
;将构造函数名称分配给新对象的属性
constructor
。Object.create
仅执行第一步
和第二
步骤!在所提供的代码示例中,这没什么大不了的,但在下一个示例中,它是:
副作用结果将是:
因为
Guest.prototype = new SiteMember();
但是我们不需要执行父构造方法,我们只需要使方法
getName
在Guest中可用。因此我们必须使用
Object.create
。如果替换
Guest.prototype = new SiteMember();
Guest.prototype = Object.create(SiteMember.prototype);
结果为:I think the main point in question - is to understand difference between
new
andObject.create
approaches. Accordingly to this answer and to this videonew
keyword does next things:Creates new object.
Links new object to constructor function (
prototype
).Makes
this
variable point to the new object.Executes constructor function using the new object and implicit perform
return this
;Assigns constructor function name to new object's property
constructor
.Object.create
performs only1st
and2nd
steps!!!In code example provided in question it isn't big deal, but in next example it is:
As side effect result will be:
because of
Guest.prototype = new SiteMember();
But we don't need to execute parent constructor method, we need only make method
getName
to be available in Guest.Hence we have to use
Object.create
.If replace
Guest.prototype = new SiteMember();
to
Guest.prototype = Object.create(SiteMember.prototype);
result be:有时您无法使用 NEW 创建对象,但仍然可以调用 CREATE 方法。
例如:如果您想定义自定义元素,它必须从 HTMLElement 派生。
Sometimes you cannot create an object with NEW but are still able to invoke the CREATE method.
For example: if you want to define a Custom Element it must derive from HTMLElement.
优点是,在大多数浏览器上,
Object.create
通常比new
慢在此 jsperf 示例中,在 Chromium 中,浏览器
new
的速度 30 倍 code>Object.create(obj) 虽然两者都相当快。这很奇怪,因为 new 做了更多的事情(比如调用构造函数),而 Object.create 应该只是用传入的对象作为原型创建一个新对象(Crockford 语言中的秘密链接)也许浏览器还没有跟上使
Object.create
更加高效(也许他们在幕后基于new
......即使在本机代码中也是如此)The advantage is that
Object.create
is typically slower thannew
on most browsersIn this jsperf example, in a Chromium, browser
new
is 30 times as fast asObject.create(obj)
although both are pretty fast. This is all pretty strange because new does more things (like invoking a constructor) where Object.create should be just creating a new Object with the passed in object as a prototype (secret link in Crockford-speak)Perhaps the browsers have not caught up in making
Object.create
more efficient (perhaps they are basing it onnew
under the covers ... even in native code)摘要:
Object.create()
是一个 Javascript 函数,它接受 2 个参数并返回一个新对象。示例:
实际应用:
new
关键字会调用构造函数。使用Object.create()
,不需要调用甚至声明构造函数。Summary:
Object.create()
is a Javascript function which takes 2 arguments and returns a new object.Example:
Practical applications:
new
keyword you have no control over this (however, you can overwrite them of course).new
keyword invokes a constructor function. WithObject.create()
there is no need for invoking or even declaring a constructor function.new
运算new
关键字还执行构造函数Object.create
Object.create
创建一个新对象Object.create
用于从另一个对象创建一个对象new
Operatornew
keywords also executes the constructor functionObject.create
Object.create
to create a new objectObject.create
is used to create an object from another object您必须创建一个自定义
Object.create()
函数。一种可以解决 Crockfords 问题并调用您的 init 函数的方法。这将起作用:
这里 UserB 就像 Object.create,但根据我们的需要进行了调整。
如果您愿意,您也可以致电:
You have to make a custom
Object.create()
function. One that addresses Crockfords concerns and also calls your init function.This will work:
Here UserB is like Object.create, but adjusted for our needs.
If you want, you can also call:
虽然 Douglas Crockford 曾经是 Object.create() 的热心拥护者,并且他基本上是这个构造实际上出现在 javascript 中的原因,但他不再持有这种观点。
他停止使用 Object.create,因为他完全停止使用 this 关键字,因为它会带来太多麻烦。例如,如果您不小心,它很容易指向全局对象,这可能会产生非常严重的后果。他声称,如果不使用 this Object.create 就不再有意义了。
您可以查看 2014 年他在 Nordic.js 上演讲的视频:
https://www.youtube .com/watch?v=PSGEjv3Tqo0
While Douglas Crockford used to be a zealous advocate of Object.create() and he is basically the reason why this construct actually is in javascript, he no longer has this opinion.
He stopped using Object.create, because he stopped using this keyword altogether as it causes too much trouble. For example, if you are not careful it can easily point to the global object, which can have really bad consequences. And he claims that without using this Object.create does not make sense anymore.
You can check this video from 2014 where he talks at Nordic.js:
https://www.youtube.com/watch?v=PSGEjv3Tqo0
new
和Object.create
有不同的用途。new
旨在创建对象类型的新实例。Object.create
旨在简单地创建一个新对象并设置其原型。为什么这有用?无需访问__proto__
属性即可实现继承。对象实例的原型称为[[Prototype]]
,是虚拟机的内部属性,不能直接访问。实际上可以直接访问[[Prototype]]
作为__proto__
属性的唯一原因是它一直是每个主要虚拟机实现的事实上的标准ECMAScript,此时删除它会破坏许多现有代码。为了回应 7ochem 上面的答案,对象绝对不应该将其原型设置为 new 语句的结果,不仅因为多次调用相同的原型构造函数没有意义,而且因为两个实例如果原型在创建后被修改,同一类的原型可能会产生不同的行为。这两个示例都是由于误解和破坏原型继承链的预期行为而导致的错误代码。
实例的原型应该在使用
Object.create
创建时或之后使用Object.setPrototypeOf
写入,而不是访问__proto__
,并且使用Object.getPrototypeOf
或Object.isPrototypeOf
读取。另外,正如 Object.setPrototypeOf 的 Mozilla 文档 指出,出于性能原因,在创建对象后修改其原型是一个坏主意,此外,在创建对象后修改其原型可能会导致未定义的行为,如果给定的一段访问它的代码可以在原型修改之前或之后执行,除非该代码非常仔细地检查当前原型或不访问两者之间不同的任何属性。
给定
const X = 函数 (v) { this.v = v };
X.prototype.whatAmI = 'X';
X.prototype.getWhatIAm = () =>这是什么?
X.prototype.getV = () =>; this.v;
以下 VM 伪代码相当于语句
const x0 = new X(1);
:const x0 = {};
x0.[[原型]] = X.原型;
X.prototype.constructor.call(x0, 1);
请注意,虽然构造函数可以返回任何值,但
new
语句始终忽略其返回值并返回对新创建对象的引用。下面的伪代码相当于语句
const x1 = Object.create(X.prototype);
:const x0 = {};
x0.[[原型]] = X.prototype;
正如你所看到的,两者之间唯一的区别是
Object.create
不执行构造函数,它实际上可以返回任何值,而只是返回新的对象引用this
如果没有另外指定。现在,如果我们想创建一个具有以下定义的子类 Y:
const Y = 函数(u) { this.u = u; }
Y.prototype.whatAmI = 'Y';
Y.prototype.getU = () =>; this.u;
然后我们可以通过写入
__proto__
来让它继承X:Y.prototype.__proto__ = X.prototype;
虽然无需使用以下命令写入
__proto__
也可以完成同样的事情:Y.prototype = Object.create(X.prototype);
Y.prototype.constructor = Y;
在后一种情况下,需要设置原型的构造函数属性,以便
new Y
语句调用正确的构造函数,否则new Y
将调用该函数X
。如果程序员确实希望new Y
调用X
,那么在 Y 的构造函数中使用X.call(this, u)
会更合适。new
andObject.create
serve different purposes.new
is intended to create a new instance of an object type.Object.create
is intended to simply create a new object and set its prototype. Why is this useful? To implement inheritance without accessing the__proto__
property. An object instance's prototype referred to as[[Prototype]]
is an internal property of the virtual machine and is not intended to be directly accessed. The only reason it is actually possible to directly access[[Prototype]]
as the__proto__
property is because it has always been a de-facto standard of every major virtual machine's implementation of ECMAScript, and at this point removing it would break a lot of existing code.In response to the answer above by 7ochem, objects should absolutely never have their prototype set to the result of a
new
statement, not only because there's no point calling the same prototype constructor multiple times but also because two instances of the same class can end up with different behavior if one's prototype is modified after being created. Both examples are simply bad code as a result of misunderstanding and breaking the intended behavior of the prototype inheritance chain.Instead of accessing
__proto__
, an instance's prototype should be written to when an it is created withObject.create
or afterward withObject.setPrototypeOf
, and read withObject.getPrototypeOf
orObject.isPrototypeOf
.Also, as the Mozilla documentation of Object.setPrototypeOf points out, it is a bad idea to modify the prototype of an object after it is created for performance reasons, in addition to the fact that modifying an object's prototype after it is created can cause undefined behavior if a given piece of code that accesses it can be executed before OR after the prototype is modified, unless that code is very careful to check the current prototype or not access any property that differs between the two.
Given
const X = function (v) { this.v = v };
X.prototype.whatAmI = 'X';
X.prototype.getWhatIAm = () => this.whatAmI;
X.prototype.getV = () => this.v;
the following VM pseudo-code is equivalent to the statement
const x0 = new X(1);
:const x0 = {};
x0.[[Prototype]] = X.prototype;
X.prototype.constructor.call(x0, 1);
Note although the constructor can return any value, the
new
statement always ignores its return value and returns a reference to the newly created object.And the following pseudo-code is equivalent to the statement
const x1 = Object.create(X.prototype);
:const x0 = {};
x0.[[Prototype]] = X.prototype;
As you can see, the only difference between the two is that
Object.create
does not execute the constructor, which can actually return any value but simply returns the new object referencethis
if not otherwise specified.Now, if we wanted to create a subclass Y with the following definition:
const Y = function(u) { this.u = u; }
Y.prototype.whatAmI = 'Y';
Y.prototype.getU = () => this.u;
Then we can make it inherit from X like this by writing to
__proto__
:Y.prototype.__proto__ = X.prototype;
While the same thing could be accomplished without ever writing to
__proto__
with:Y.prototype = Object.create(X.prototype);
Y.prototype.constructor = Y;
In the latter case, it is necessary to set the constructor property of the prototype so that the correct constructor is called by the
new Y
statement, otherwisenew Y
will call the functionX
. If the programmer does wantnew Y
to callX
, it would be more properly done in Y's constructor withX.call(this, u)
我更喜欢封闭的方法。
我仍然使用
new
。我不使用
Object.create
。我不使用
这个
。我仍然使用
new
因为我喜欢它的声明性本质。考虑这个简单的继承。
鼓励反馈。
I prefer a closure approach.
I still use
new
.I don't use
Object.create
.I don't use
this
.I still use
new
as I like the declarative nature of it.Consider this for simple inheritance.
Feedback encouraged.