使用“Object.create”而不是“new”
JavaScript 1.9.3 / ECMAScript 5引入了Object.create
,道格拉斯克罗克福德等人长期以来一直主张。 如何用Object.create
在下面的代码中替换new
的?
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'?
只有一个级别的继承,你的例子可能不会让你看到Object.create
的真正好处。
此方法允许您轻松实现差异继承,其中对象可以直接从其他对象继承。
在你的userB
例子中,我不认为你的init
方法应该是public或者甚至是存在的,如果你在现有的对象实例上再次调用这个方法, id
和name
属性将会改变。
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
}
});
如您所见,可以使用与Object.defineProperties
和Object.defineProperty
方法类似的语法在Object.create
的第二个参数上初始化属性,并使用对象文字。
它允许您设置属性属性( enumerable
, writable
或可configurable
),这可能非常有用。
在Object.create(...)
上使用new object
实际上没有优势。
那些提倡这种方法的人通常会表现出相当含糊的优点:“可扩展性”或“对JavaScript更自然”等。
但是,我还没有看到一个具体的例子,显示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!!!
发生这种情况是因为Object.create(...)
提倡使用数据创建新对象的做法; 这里的Animal
数据成为lion
和bird
的原型的一部分,并且在共享时引起问题。 当使用新的原型继承是明确的:
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
关于传递给Object.create(...)
的可选属性属性,可以使用Object.defineProperties(...)
添加这些属性。
Object.create在几个浏览器上还不是标准的,例如IE8,Opera v11.5,Konq 4.3都没有。 对于这些浏览器,您可以使用Douglas Crockford的Object.create版本,但这不包括CMS答案中使用的第二个“初始化对象”参数。
对于跨浏览器代码,同时获得对象初始化的一种方法是自定义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()
}
这保持了Crockford的原型继承,并且还检查对象中的任何init方法,然后使用参数运行它,就像说新人('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();
所以bob继承了sayHello方法,现在拥有自己的属性id = 1和name ='Bob'。 这些属性当然都是可写的和可枚举的。 如果您不关心可写,可枚举和可配置的属性,这也是一种比ECMA Object.create更简单的初始化方法。
对于没有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()
}
这将按照它们定义的顺序填充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());
比Object.build稍微简单些,因为userB不需要init方法。 另外,userB并不是专门的构造函数,但看起来像一个普通的单例对象。 所以用这个方法你可以从普通的普通对象构造和初始化。
链接地址: http://www.djcxy.com/p/79169.html上一篇: Using "Object.create" instead of "new"
下一篇: Passing arguments to C# generic new() of templated type