Javascript 使用“Object.create”而不是“new”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2709612/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using "Object.create" instead of "new"
提问by Graham King
Javascript 1.9.3 / ECMAScript 5 introduces Object.create, which Douglas Crockford amongst others has been advocatingfor a long time. How do I replace newin the code below with Object.create?
Javascript 1.9.3 / ECMAScript 5 引入了Object.createDouglas 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();
(Assume MY_GLOBAL.nextIdexists).
(假设MY_GLOBAL.nextId存在)。
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.createto create user 'bob'?
似乎没有任何优势,所以我想我没有得到它。我可能太新古典主义了。我应该如何使用Object.create来创建用户“bob”?
采纳答案by CMS
With only one level of inheritance, your example may not let you see the real benefits of Object.create.
只有一层继承,您的示例可能无法让您看到Object.create.
This methods allows you to easily implement differential inheritance, where objects can directly inherit from other objects.
这些方法允许您轻松实现差异继承,其中对象可以直接从其他对象继承。
On your userBexample, I don't think that your initmethod should be public or even exist, if you call again this method on an existing object instance, the idand nameproperties will change.
在您的userB示例中,我认为您的init方法不应该是公共的,甚至不应该存在,如果您在现有对象实例上再次调用此方法,则id和name属性将发生变化。
Object.createlets you initialize object properties using its second argument, e.g.:
Object.create允许您使用其第二个参数初始化对象属性,例如:
var userB = {
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB, {
'id' : {
value: MY_GLOBAL.nextId(),
enumerable:true // writable:false, configurable(deletable):false by default
},
'name': {
value: 'Bob',
enumerable: true
}
});
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 the Object.definePropertiesand Object.definePropertymethods.
如您所见,属性可以在 的第二个参数上初始化Object.create,对象字面量使用的语法类似于Object.defineProperties和Object.defineProperty方法使用的语法。
It lets you set the property attributes (enumerable, writable, or configurable), which can be really useful.
它可以让你设置属性的属性(enumerable,writable,或configurable),它可以是非常有用的。
回答by Noel Abrahams
There is really no advantage in using Object.create(...)over new object.
使用Object.create(...)over确实没有优势new object。
Those advocating this method generally state rather ambiguous advantages: "scalability", or "more natural to JavaScript" etc.
那些提倡这种方法的人通常会陈述相当模糊的优势:“可扩展性”或“对 JavaScript 更自然”等。
However, I have yet to see a concrete example that shows that Object.createhas anyadvantages over using new. On the contrary there are known problems with it. Sam Elsamman describes what happens when there are nested objects and Object.create(...)is used:
但是,我还没有看到一个具体的例子那就说明Object.create有任何过度使用的优点new。相反,它存在已知的问题。Sam Elsamman 描述了Object.create(...)使用嵌套对象时会发生什么:
var Animal = {
traits: {},
}
var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!
This occurs because Object.create(...)advocates a practice where datais used to create new objects; here the Animaldatum becomes part of the prototype of lionand bird, and causes problems as it is shared. When using new the prototypal inheritance is explicit:
发生这种情况是因为Object.create(...)提倡使用数据创建新对象的做法;这里的Animal基准变的原型的部分lion和bird,并且当它是共享的会引起问题。使用 new 时,原型继承是明确的:
function Animal() {
this.traits = {};
}
function Lion() { }
Lion.prototype = new Animal();
function Bird() { }
Bird.prototype = new Animal();
var lion = new Lion();
lion.traits.legs = 4;
var bird = new Bird();
bird.traits.legs = 2;
alert(lion.traits.legs) // now shows 4
Regarding, the optional property attributes that are passed into Object.create(...), these can be added using Object.defineProperties(...).
关于传入 的可选属性属性,Object.create(...)可以使用Object.defineProperties(...).
回答by John
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.
Object.create 在一些浏览器上还不是标准的,例如 IE8、Opera v11.5、Konq 4.3 没有它。您可以为这些浏览器使用 Douglas Crockford 的 Object.create 版本,但这不包括 CMS 答案中使用的第二个“初始化对象”参数。
For cross browser code one way to get object initialisation in the meantime is to customise Crockford's Object.create. Here is one method:-
对于跨浏览器代码,同时获取对象初始化的一种方法是自定义 Crockford 的 Object.create。这是一种方法:-
Object.build = function(o) {
var initArgs = Array.prototype.slice.call(arguments,1)
function F() {
if((typeof o.init === 'function') && initArgs.length) {
o.init.apply(this,initArgs)
}
}
F.prototype = o
return new F()
}
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:-
这维护了 Crockford 原型继承,并检查对象中的任何 init 方法,然后使用您的参数运行它,例如 new man('John','Smith')。然后您的代码变为:-
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.build(userB, 'Bob'); // Different from your code
bob.sayHello();
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.
所以 bob 继承了 sayHello 方法,现在有了自己的属性 id=1 和 name='Bob'。当然,这些属性既可写又可枚举。这也是一种比 ECMA Object.create 更简单的初始化方式,尤其是当您不关心可写、可枚举和可配置属性时。
For initialisation without an init method the following Crockford mod could be used:-
对于没有 init 方法的初始化,可以使用以下 Crockford mod:-
Object.gen = function(o) {
var makeArgs = arguments
function F() {
var prop, i=1, arg, val
for(prop in o) {
if(!o.hasOwnProperty(prop)) continue
val = o[prop]
arg = makeArgs[i++]
if(typeof arg === 'undefined') break
this[prop] = arg
}
}
F.prototype = o
return new F()
}
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.
这将按照定义的顺序填充 userB 自己的属性,在 userB 参数之后从左到右使用 Object.gen 参数。它使用 for(prop in o) 循环,因此,根据 ECMA 标准,不能保证属性枚举的顺序与属性定义的顺序相同。但是,在 (4) 主要浏览器上测试的几个代码示例表明它们是相同的,前提是使用了 hasOwnProperty 过滤器,有时甚至不使用。
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}; // For example
var userB = {
name: null,
id: null,
sayHello: function() {
console.log('Hello '+ this.name);
}
}
var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId());
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.
我会说比 Object.build 简单一些,因为 userB 不需要 init 方法。此外,userB 不是专门的构造函数,而是看起来像一个普通的单例对象。因此,使用此方法,您可以从普通的普通对象构造和初始化。
回答by Nami WANG
TL;DR:
特尔;博士:
new Computer()will invoke the constructor function Computer(){}for one time, while Object.create(Computer.prototype)won't.
new Computer()将调用构造函数Computer(){}一次,而Object.create(Computer.prototype)不会。
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 than Object.create.
关于性能的旁注:构造函数调用 likenew Computer()由引擎进行了大量优化,因此它可能比Object.create.
回答by samfrances
You could make the initmethod return this, and then chain the calls together, like this:
您可以使init方法 return this,然后将调用链接在一起,如下所示:
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
return this;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB).init('Bob');
回答by basos
Another possible usage of Object.create is to clone immutable objects in a cheap and effective way.
Object.create 的另一种可能用法是以廉价且有效的方式克隆不可变对象。
var anObj = {
a: "test",
b: "jest"
};
var bObj = Object.create(anObj);
bObj.b = "gone"; // replace an existing (by masking prototype)
bObj.c = "brand"; // add a new to demonstrate it is actually a new obj
// now bObj is {a: test, b: gone, c: brand}
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.
注意:上面的代码片段创建了一个源对象的克隆(又名不是引用,如 cObj = aObj)。它优于 copy-properties 方法(参见1),因为它不复制对象成员属性。相反,它创建了另一个 -destination- 对象,其原型设置在源对象上。此外,当 dest 对象上的属性被修改时,它们会“即时”创建,掩盖原型(src 的)属性。这构成了一种快速有效的克隆不可变对象的方法。
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).
Fiddle here(http://jsfiddle.net/y5b5q/1/)(需要支持 Object.create 的浏览器)。
回答by V. Kovpak
I think the main point in question - is to understand difference between newand Object.createapproaches. Accordingly to this answerand to this videonewkeyword does next things:
我认为问题的主要观点 - 是了解new和Object.create方法之间的区别。根据这个答案和这个视频new关键字做接下来的事情:
Creates new object.
Links new object to constructor function (
prototype).Makes
thisvariable 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.
创建新对象。
将新对象链接到构造函数 (
prototype)。使
this变量指向新对象。使用新对象和隐式执行执行构造函数
return this;将构造函数名称分配给新对象的属性
constructor。
Object.createperforms only 1stand 2ndsteps!!!
Object.create只执行1st和2nd步骤!!!
In code example provided in question it isn't big deal, but in next example it is:
在有问题的代码示例中,这没什么大不了的,但在下一个示例中,它是:
var onlineUsers = [];
function SiteMember(name) {
this.name = name;
onlineUsers.push(name);
}
SiteMember.prototype.getName = function() {
return this.name;
}
function Guest(name) {
SiteMember.call(this, name);
}
Guest.prototype = new SiteMember();
var g = new Guest('James');
console.log(onlineUsers);
As side effect result will be:
由于副作用结果将是:
[ undefined, 'James' ]
because of Guest.prototype = new SiteMember();
But we don't need to execute parent constructor method, we need only make method getNameto 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:
因为Guest.prototype = new SiteMember();
但是我们不需要执行父构造函数方法,我们只需要getName在Guest中可用的make方法。因此我们必须使用Object.create.
如果更换Guest.prototype = new SiteMember();
到Guest.prototype = Object.create(SiteMember.prototype);结果是:
[ 'James' ]
回答by Supersharp
Sometimes you cannot create an object with NEW but are still able to invoke the CREATE method.
有时您无法使用 NEW 创建对象,但仍然可以调用 CREATE 方法。
For example: if you want to define a Custom Element it must derive from HTMLElement.
例如:如果你想定义一个自定义元素,它必须从 HTMLElement 派生。
proto = new HTMLElement //fail :(
proto = Object.create( HTMLElement.prototype ) //OK :)
document.registerElement( "custom-element", { prototype: proto } )
回答by frooble
The advantage is that Object.createis typically slower than newon most browsers
优点是Object.create通常比new大多数浏览器慢
In this jsperf example, in a Chromium, browser newis 30 times as fastas Object.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)
在此jsperf例如,在铬,浏览器new是30倍的速度作为Object.create(obj)虽然两者都是相当快。这一切都很奇怪,因为 new 做了更多的事情(比如调用构造函数),其中 Object.create 应该只是创建一个新对象,并将传入的对象作为原型(Crockford 说的秘密链接)
Perhaps the browsers have not caught up in making Object.createmore efficient (perhaps they are basing it on newunder the covers ... even in native code)
也许浏览器没有赶上提高Object.create效率(也许他们是new在幕后……甚至在本机代码中)
回答by edwin
You have to make a custom Object.create()function. One that addresses Crockfords concerns and also calls your init function.
您必须创建自定义Object.create()函数。一种解决 Crockfords 问题并调用您的 init 函数的方法。
This will work:
这将起作用:
var userBPrototype = {
init: function(nameParam) {
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
function UserB(name) {
function F() {};
F.prototype = userBPrototype;
var f = new F;
f.init(name);
return f;
}
var bob = UserB('bob');
bob.sayHello();
Here UserB is like Object.create, but adjusted for our needs.
这里 UserB 类似于 Object.create,但根据我们的需要进行了调整。
If you want, you can also call:
如果你愿意,你也可以调用:
var bob = new UserB('bob');

