2010-11-12 27 views
308

我最近偶然发现了JavaScript中的Object.create()方法,并试图推导出它与创建一个对象的新实例的方式不同new SomeFunction(),以及当您会想要使用其中一个。了解Object.create()和新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/

+2

请参阅[使用“Object.create”而不是“new”](http:// stackoverflow。com/q/2709612/1048572) – Bergi 2015-07-24 15:04:23

回答

205

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方法,而不是克罗克福德的垫片。

该方法开始在最新的浏览器上本地执行,请检查此compatibility table

+1

@CMS 2个问题。 1)Object.create(null)上的作用域链是否仍然在全局作用域(比如浏览器中的'window')终止,还是终止于它本身? 2)我还不清楚为什么引入了Object.create(例如,什么功能被遗漏了,这就解决了这个问题?)以及为什么会用它来代替新的Function(); – Matt 2010-11-12 16:30:12

+8

@Matt,1)范围链在这里并不是一个真正的相关概念,范围链与**标识符解析**有关,例如:在当前的*词法环境中如何解析'foo;'。 2)为了提供一个简单的方法来实现继承,这是一个非常强大的构造。 IMO我会使用它,因为它非常简单和轻量级,但对于生产代码,我们仍然需要等待一段时间,直到ES5得到广泛支持。关于缺少的功能,创建“原始”对象“Object.create(null);”的事实丢失了,实现可靠的类似哈希表的对象是非常有用的... – CMS 2010-11-12 16:38:32

+0

@CMS谢谢! – Matt 2010-11-12 17:27:37

7

内部Object.create做到这一点:

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

语法只是带走了错觉,JavaScript使用经典的继承。

+24

ECMAScript 5 ['Object.create'](http://sideshowbarker.github.com/es5-spec/#x15.2.3.5)方法的功能远不止于此,您可以通过*属性描述符定义属性*,并且可以创建一个不会从任何东西继承的对象(Object.create(null);'),应该避免使用这种类型的垫片,因为您无法真正模拟该行为在ES3上。 [更多信息](http://stackoverflow.com/questions/3830800/object-defineproperty-in-es5/3844768#3844768) – CMS 2010-11-12 16:24:00

+0

嗯,不知道。 – xj9 2010-11-12 19:25:03

+0

同意@CMS,但一般来说,它是Object.create的简单填充。 – 2017-07-27 21:02:09

166

这里有两个电话内部所发生的步骤:
(提示:唯一的区别是在步骤3)


new Test()

  1. 创建new Object() OBJ
  2. obj.__proto__Test.prototype
  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__Test.prototype
  3. return obj;

因此基本上Object.create不执行构造函数。

+4

非常有帮助的比较。 – 2015-03-09 22:30:36

+0

@射线如此使用object.create我们字体有构造函数中提到的函数的属性? – 2017-02-21 12:28:10

+0

@sortednoun只要属性是私人的,并没有在原型上指定,**是的,他们不会被继承,你不会让他们在新的对象**(和,我会补充,你可以期望以便在父构造函数至少执行一次的情况下从父类获得最终的原型属性)。 – Kamafeather 2017-07-25 16:52:45

16

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

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

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

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

请参阅this link来比较两种方式。

+0

这个答案也很好。 +1 – Bento 2016-02-02 17:42:58

297

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

就是这样。 :)

其余的答案只是令人困惑,因为显然没有人读取new的定义。)

+18

+1简单明了! (尽管Object.create(null)似乎是一个不错的选择 - 可能应该提到这一点)。 – user949300 2014-09-03 04:15:46

+1

最佳答案我见过。非常有帮助! – m0meni 2016-01-26 15:02:14

+0

保持简单就是要走的路 – Bruce 2016-05-26 12:12:04

20
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关键字有两点需要注意;

一)函数是用来作为构造

B)function.prototype对象传递给__proto__财产......或者__proto__不被支持,这是第二个地方,新的对象看起来找到性能

2)Object.create(obj.prototype)你正在构造一个对象(obj.prototype),并将它传递给其计划的目的..with,现在新对象的__proto__也指向obj.prototype的差异(请xj9为REF ANS)

31

此:

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()双参数版本,那么你可以做更多的强大的东西。

+1

很好的解释。我可以添加,像这样以最简单的形式使用Object.create,可以在利用原型继承的同时,从代码中省略构造函数。 – 2015-11-19 04:38:38

51

让我试着解释(更多Blog):

  1. 当你写Car构造var Car = function(){},这是怎么回事儿内部: A diagram of prototypal chains when creating javascript objects 我们一个{prototype}隐藏链接Function.prototype这是不可访问和一个prototype链接到Car.prototype这是访问,并有一个实际constructorCar。 Function.prototype和Car.prototype都隐藏了Object.prototype的链接。
  2. 当我们想通过使用new运算符和create方法创建两个等价对象时,我们必须这样做:Honda = new Car();Maruti = Object.create(Car.prototype)A diagram of prototypal chains for differing object creation methods 发生了什么事?

    Honda = new Car(); —当您创建这样便隐藏{prototype}属性的对象指向Car.prototype。所以在这里,本田物件的{prototype}将永远是Car.prototype —我们没有任何选择来更改物件的{prototype}属性。如果我想改变我们新创建的对象的原型呢?
    Maruti = Object.create(Car.prototype) —当你创建一个这样的对象时,你有一个额外的选项来选择你的对象的{prototype}属性。如果您想将Car.prototype作为{prototype},则将其作为函数中的参数传递。如果你不想要任何{prototype}对象,那么你可以通过null像这样:Maruti = Object.create(null)

结论—利用该方法Object.create你可以自由选择你的对象{prototype}财产。在new Car();中,你没有那种自由。在OO的JavaScript

首选方式:

假设我们有两个对象ab

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

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

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

问题—我们要反对ab原型,但在这里我们创建的对象b与原型Object.prototype解决方案— 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()是继承是非常有用的。

+0

好。我认为这是触及牛眼的答案 – Bento 2016-02-02 17:14:23

+0

因此,它与没有构造函数调用的对象创建有些类似? 我们将享受该课程的所有好处。 obj instanceof类也是如此。但我们不是通过new调用Class函数。 – Praveen 2016-03-30 08:49:09

+0

@Anshul你说'a.isPrototypeOf(b);'会返回false,这是正确的,因为两个对象都不同并且指向不同的内存。使用'new'操作符的正确方法就在这里。 - https://jsfiddle.net/167ununp/。 – 2016-11-10 11:29:19

3

因此对this answerthis videonew关键字做接下来的事情就:

  1. 创建新的对象。

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

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

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

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

Object.create只执行1st2nd步骤!

相关问题