使用“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或者甚至是存在的,如果你在现有的对象实例上再次调用这个方法, idname属性将会改变。

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.definePropertiesObject.defineProperty方法类似的语法在Object.create的第二个参数上初始化属性,并使用对象文字。

它允许您设置属性属性( enumerablewritable或可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数据成为lionbird的原型的一部分,并且在共享时引起问题。 当使用新的原型继承是明确的:

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