理解Object.create()和new SomeFunction()之间的区别

我最近偶然发现了JavaScript中的Object.create()方法,试图推导出它与使用new SomeFunction()创建对象的新实例的方式不同,以及何时想要使用另一个方法。

考虑下面的例子:

 var test = { val: 1, func: function() { return this.val; } }; var testA = Object.create(test); testA.val = 2; console.log(test.func()); // 1 console.log(testA.func()); // 2 console.log('other test'); var otherTest = function() { this.val = 1; this.func = function() { return this.val; }; }; var otherTestA = new otherTest(); var otherTestB = new otherTest(); otherTestB.val = 2; console.log(otherTestA.val); // 1 console.log(otherTestB.val); // 2 console.log(otherTestA.func()); // 1 console.log(otherTestB.func()); // 2 

请注意,在这两种情况下都观察到相同的行为。 在我看来,这两种情况的主要区别是:

  • Object.create()使用的对象实际上形成了新对象的原型,而在来自声明的属性/函数的new Function()却没有形成原型。
  • 您不能像使用函数语法那样使用Object.create()语法创建闭包。 根据JavaScript的词法(vs block)类型,这是合乎逻辑的。

上述说法是否正确? 我错过了什么? 你什么时候使用一个?

编辑:链接到jsfiddle版本的上述代码示例: http : //jsfiddle.net/rZfYL/

Object.create中使用的对象实际上形成了新对象的原型,在新的Function()形式中,声明的属性/函数不构成原型。

是的, Object.create构建一个直接从作为第一个参数传递的对象继承的对象。

使用构造函数,新创建的对象从构造函数的原型继承,例如:

 var o = new SomeConstructor(); 

在上面的例子中, o直接从SomeConstructor.prototype继承。

这里有一个区别,用Object.create你可以创建一个不会从任何东西继承的对象, Object.create(null); 另一方面,如果你设置SomeConstructor.prototype = null; 新创建的对象将继承Object.prototype

您不能像使用函数语法一样使用Object.create语法来创建闭包。 根据JavaScript的词法(vs block)类型,这是合乎逻辑的。

那么,你可以创建闭包,例如使用属性描述符参数:

 var o = Object.create({inherited: 1}, { foo: { get: (function () { // a closure var closured = 'foo'; return function () { return closured+'bar'; }; })() } }); o.foo; // "foobar" 

请注意,我正在讨论ECMAScript第5版Object.create方法,而不是Crockford的垫片。

该方法开始在最新的浏览器上本地实现,请检查此兼容性表 。

很简单地说, new XObject.create(X.prototype) ,另外运行constructor函数。 (给constructor return应该是表达式结果的实际对象而不是this 。)

而已。 🙂

其余的答案只是混乱,因为显然没有人读到new的定义。 ;)

以下是这两个调用内部发生的步骤:
(提示:唯一的区别是在步骤3)


new Test()

  1. 创建new Object() obj
  2. obj.__proto__设置为obj.__proto__
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. 创建new Object() obj
  2. obj.__proto__设置为obj.__proto__
  3. return obj;

所以基本上Object.create不执行构造函数。

让我试着解释(更多博客 ):

  1. 当你写Car构造函数var Car = function(){} ,这是怎么东西在内部: 创建JavaScript对象时的原型链图 我们有一个{prototype}隐藏的链接到Function.prototype不可访问和一个prototype链接到Car.prototype可访问,并有一个Car的实际constructor 。 Function.prototype和Car.prototype都隐藏了Object.prototype链接。
  2. 当我们想通过使用new运算符和create方法创建两个等价对象时,我们必须这样做: Honda = new Car();Maruti = Object.create(Car.prototype)不同对象创建方法的原型链图 发生什么事?

    Honda = new Car(); – 当你创建一个像这样的对象时,隐藏的{prototype}属性被指向Car.prototype 。 所以在这里,Honda对象的{prototype}将始终是Car.prototype – 我们没有任何选择来更改对象的{prototype}属性。 如果我想改变我们新创建的对象的原型呢?
    Maruti = Object.create(Car.prototype) – 当你像这样创建一个对象时,你有一个额外的选项来选择你的对象的{prototype}属性。 如果你想把Car.prototype作为{prototype}然后把它作为参数传递给函数。 如果你不想为你的对象使用任何{prototype} ,那么你可以像这样传递nullMaruti = Object.create(null)

结论 – 通过使用Object.create方法,您可以自由选择您的对象{prototype}属性。 new Car(); ,你没有这种自由。

OO JavaScript中的首选方法:

假设我们有两个对象ab

 var a = new Object(); var b = new Object(); 

现在,假设a有一些b也想访问的方法。 为此,我们需要对象继承(如果我们想访问这些方法, b应该是b的原型)。 如果我们检查ab的原型,那么我们会发现他们共享原型Object.prototype

 Object.prototype.isPrototypeOf(b); //true a.isPrototypeOf(b); //false (the problem comes into the picture here). 

问题 –我们想要将对象a作为b的原型,但是在这里我们用原型Object.prototype创建了对象b解决方案 – ECMAScript 5引入了Object.create() ,轻松实现了这种继承。 如果我们这样创建对象b

 var b = Object.create(a); 

然后,

 a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.) 

所以,如果你正在做面向对象的脚本,那么Object.create()对于继承是非常有用的。

这个:

 var foo = new Foo(); 

 var foo = Object.create(Foo.prototype); 

非常相似。 一个重要的区别是new Foo实际上运行构造函数代码,而Object.create不会执行代码

 function Foo() { alert("This constructor does not run with Object.create"); } 

请注意,如果使用Object.create()的双参数版本,则可以执行更强大的功能。

 function Test(){ this.prop1 = 'prop1'; this.prop2 = 'prop2'; this.func1 = function(){ return this.prop1 + this.prop2; } }; Test.prototype.protoProp1 = 'protoProp1'; Test.prototype.protoProp2 = 'protoProp2'; var newKeywordTest = new Test(); var objectCreateTest = Object.create(Test.prototype); /* Object.create */ console.log(objectCreateTest.prop1); // undefined console.log(objectCreateTest.protoProp1); // protoProp1 console.log(objectCreateTest.__proto__.protoProp1); // protoProp1 /* new */ console.log(newKeywordTest.prop1); // prop1 console.log(newKeywordTest.__proto__.protoProp1); // protoProp1 

概要:

1)用new关键字有两件事要注意;

a)函数被用作构造函数

b) function.prototype对象被传递给__proto__属性…或者__proto__不被支持,它是新对象查找属性的第二个地方

2)用Object.create(obj.prototype)你正在构造一个对象( obj.prototype ),并将其传递给目标对象..不同之处是现在新对象的__proto__也指向obj.prototype(请参考xj9)

所不同的是所谓的“伪古典与原型遗传”。 建议只在代码中使用一种类型,而不是混合两种。

在伪古典继承中(用“新”运算符),假设你先定义一个伪类,然后从这个类创建对象。 例如,定义一个伪类“Person”,然后从“Person”创建“Alice”和“Bob”。

在原型继承(使用Object.create)中,您直接创建一个特定的人“Alice”,然后使用“Alice”创建另一个人“Bob”作为原型。 这里没有“阶级” 都是对象。

JavaScript内部使用“原型继承”; “伪古典”的方式只是一些糖。

看到这个链接比较两种方式。

Object.create在内部做到这一点:

 Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; 

这个语法只是消除了JavaScript使用古典继承的错觉。

根据这个答案和这个视频 new关键字做下一件事情:

  1. 创建新的对象。

  2. 将新对象链接到构造函数( prototype )。

  3. 使this变量指向新的对象。

  4. 使用新对象执行构造函数并隐式执行return this ;

  5. 将构造函数名称分配给新对象的属性constructor

Object.create只执行1st2nd