理解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 X
是Object.create(X.prototype)
,另外运行constructor
函数。 (给constructor
return
应该是表达式结果的实际对象而不是this
。)
而已。 🙂
其余的答案只是混乱,因为显然没有人读到new
的定义。 ;)
以下是这两个调用内部发生的步骤:
(提示:唯一的区别是在步骤3)
new Test()
:
- 创建
new Object()
obj - 将
obj.__proto__
设置为obj.__proto__
-
return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value
Object.create( Test.prototype )
- 创建
new Object()
obj - 将
obj.__proto__
设置为obj.__proto__
-
return obj;
所以基本上Object.create
不执行构造函数。
让我试着解释(更多博客 ):
- 当你写
Car
构造函数var Car = function(){}
,这是怎么东西在内部: 我们有一个{prototype}
隐藏的链接到Function.prototype
不可访问和一个prototype
链接到Car.prototype
可访问,并有一个Car
的实际constructor
。 Function.prototype和Car.prototype都隐藏了Object.prototype
链接。 -
当我们想通过使用
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}
,那么你可以像这样传递null
:Maruti = Object.create(null)
。
结论 – 通过使用Object.create
方法,您可以自由选择您的对象{prototype}
属性。 在new Car();
,你没有这种自由。
OO JavaScript中的首选方法:
假设我们有两个对象a
和b
。
var a = new Object(); var b = new Object();
现在,假设a
有一些b
也想访问的方法。 为此,我们需要对象继承(如果我们想访问这些方法, b
应该是b
的原型)。 如果我们检查a
和b
的原型,那么我们会发现他们共享原型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
关键字做下一件事情:
-
创建新的对象。
-
将新对象链接到构造函数(
prototype
)。 -
使
this
变量指向新的对象。 -
使用新对象执行构造函数并隐式执行
return this
; -
将构造函数名称分配给新对象的属性
constructor
。
Object.create
只执行1st
和2nd
!