我最近偶然发现了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块)类型范围,这是合乎逻辑的。以上陈述是否正确?我错过了什么吗?你什么时候用一个而不是另一个?
编辑:链接到上面代码示例的jsfiddle版本:http://jsfiddle.net/rZfYL/
答案 0 :(得分:376)
非常简单地说,new X
是Object.create(X.prototype)
并另外运行constructor
功能。 (并使constructor
有机会return
应该是表达式结果而不是this
的实际对象。)
就是这样。 :)
其余答案只是令人困惑,因为显然没有其他人读过new
的定义。 ;)
答案 1 :(得分:232)
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块)类型范围,这是合乎逻辑的。
嗯,你可以创建闭包,例如使用属性描述符参数:
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的垫片。
该方法已开始在最新浏览器上本机实现,请查看此compatibility table。
答案 2 :(得分:195)
以下是两次调用内部发生的步骤:
(提示:唯一的区别在于步骤3)
new Test()
:
new Object()
obj obj.__proto__
设为Test.prototype
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__
设为Test.prototype
return obj;
所以基本上Object.create
不执行构造函数。
答案 3 :(得分:59)
让我试着解释一下(更多关于Blog):
Car
构造函数var Car = function(){}
时,这就是内部的事情:
我们有一个{prototype}
隐藏链接指向Function.prototype
无法访问,一个prototype
链接指向Car.prototype
,该链接可访问且实际constructor
为{{1} }}。 Function.prototype和Car.prototype都隐藏了Car
。当我们想要使用Object.prototype
运算符和new
方法创建两个等效对象时,我们必须这样做:create
和Honda = new Car();
。
发生了什么事?
Maruti = Object.create(Car.prototype)
- 当您创建此类对象时,隐藏的Honda = new Car();
属性将指向{prototype}
。所以在这里,本田对象的Car.prototype
始终为{prototype}
- 我们没有任何选项可以更改对象的Car.prototype
属性。如果我想更改新创建的对象的原型怎么办?
{prototype}
- 创建此类对象时,您可以选择对象的Maruti = Object.create(Car.prototype)
属性。如果您希望Car.prototype为{prototype}
,则将其作为参数传递给函数。如果您不想要任何{prototype}
对象,那么您可以像{prototype}
这样传递null
。
结论 - 通过使用方法Maruti = Object.create(null)
,您可以自由选择对象Object.create
属性。在{prototype}
,你没有那种自由。
OO JavaScript中的首选方式:
假设我们有两个对象new Car();
和a
。
b
现在,假设var a = new Object();
var b = new Object();
有一些a
也想要访问的方法。为此,我们需要对象继承(b
只有在我们想要访问这些方法时才应该是a
的原型)。如果我们检查b
和a
的原型,那么我们会发现它们共享原型b
。
Object.prototype
问题 - 我们希望将对象Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).
作为a
的原型,但在这里我们使用原型b
创建了对象b
。
解决方案 - ECMAScript 5引入了Object.prototype
,以轻松实现这种继承。如果我们像这样创建对象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.)
对于继承非常有用。
答案 4 :(得分:40)
此:
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()
的双参数版本,那么您可以执行更强大的功能。
答案 5 :(得分:22)
不同之处在于所谓的“伪经典与原型继承”。建议是在代码中只使用一种类型,而不是将两者混合使用。
在伪经典继承(使用“new”运算符)中,假设您首先定义一个伪类,然后从该类创建对象。例如,定义一个伪类“Person”,然后从“Person”创建“Alice”和“Bob”。
在原型继承中(使用Object.create),您直接创建一个特定的人“Alice”,然后使用“Alice”作为原型创建另一个人“Bob”。这里没有“班级”;都是对象。
在内部,JavaScript使用“原型继承”; “伪经典”的方式只是一些糖。
有关这两种方式的比较,请参阅this link。
答案 6 :(得分:21)
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
要点:
带有new
关键字的1)有两件事需要注意;
a)函数用作构造函数
b)function.prototype
对象传递给__proto__
属性...或者不支持__proto__
的地方,它是新对象查找属性的第二个地方
2)使用Object.create(obj.prototype)
构建一个对象(obj.prototype
)并将其传递给目标对象..不同之处在于现在新对象的__proto__
也指向obj。原型(请参考xj9)
答案 7 :(得分:10)
内部Object.create
执行此操作:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
语法只是消除了JavaScript使用经典继承的错觉。
答案 8 :(得分:9)
相应于this answer和this video new
关键字会做下一件事:
创建新对象。
将新对象链接到构造函数(prototype
)。
使this
变量指向新对象。
使用新对象执行构造函数并隐式执行return this
;
将构造函数名称分配给新对象的属性constructor
。
Object.create
仅执行1st
和2nd
步骤!!!
答案 9 :(得分:8)
对象创建变体。
变体1 :'新对象()' - >没有参数的对象构造函数。
var p1 = new Object(); // 'new Object()' create and return empty object -> {}
var p2 = new Object(); // 'new Object()' create and return empty object -> {}
console.log(p1); // empty object -> {}
console.log(p2); // empty object -> {}
// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false
console.log(p1.prototype); // undefined
// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}
// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}
console.log(p1.__proto__ === Object.prototype); // true
// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null
console.log(Object.prototype.__proto__); // null
变式2 :'新对象(人)' - >带参数的对象构造函数。
const person = {
name: 'no name',
lastName: 'no lastName',
age: -1
}
// 'new Object(person)' return 'person', which is pointer to the object ->
// -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);
// 'new Object(person)' return 'person', which is pointer to the object ->
// -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);
// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true
p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'
// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }
变体3.1 :' Object.create(person)'。将Object.create与简单对象'person'一起使用。 'Object.create(person)'将创建(并返回)新的空对象,并将属性'__proto__'添加到同一个新的空对象中。此属性“__proto__”将指向对象“person”。
const person = {
name: 'no name',
lastName: 'no lastName',
age: -1,
getInfo: function getName() {
return `${this.name} ${this.lastName}, ${this.age}!`;
}
}
var p1 = Object.create(person);
var p2 = Object.create(person);
// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true
console.log(person.__proto__); // {}(which is the Object.prototype)
// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);
console.log(p1); // empty object - {}
console.log(p2); // empty object - {}
// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;
// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);
// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);
// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);
// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!
// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!
变体3.2 :' Object.create(Object.prototype)'。将Object.create与内置对象一起使用 - > 'Object.prototype中'。 'Object.create(Object.prototype)'将创建(并返回)新的空对象,并将属性'__proto__'添加到同一个新的空对象中。此属性“__proto__”将指向对象“Object.prototype”。
// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);
// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);
console.log(p1); // {}
console.log(p2); // {}
console.log(p1 === p2); // false
console.log(p1.prototype); // undefined
console.log(p2.prototype); // undefined
console.log(p1.__proto__ === Object.prototype); // true
console.log(p2.__proto__ === Object.prototype); // true
变体4 :'新的SomeFunction()'
// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {
this.name = name;
this.lastName = lastName;
this.age = age;
//-----------------------------------------------------------------
// !--- only for demonstration ---
// if add function 'getInfo' into
// constructor-function 'Person',
// then all instances will have a copy of the function 'getInfo'!
//
// this.getInfo: function getInfo() {
// return this.name + " " + this.lastName + ", " + this.age + "!";
// }
//-----------------------------------------------------------------
}
// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}
// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
return this.name + " " + this.lastName + ", " + this.age + "!";
}
// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }
// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);
// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);
// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);
// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);
// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }
// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }
console.log(p1.__proto__ === p2.__proto__); // true
// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false
// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo'
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!
// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo'
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!
答案 10 :(得分:0)
Object.create(Constructor.prototype)
是new Constructor
new Constructor
的实现// 1. define constructor function
function myConstructor(name, age) {
this.name = name;
this.age = age;
}
myConstructor.prototype.greet = function(){
console.log(this.name, this.age)
};
// 2. new operator implementation
let newOperatorWithConstructor = function(name, age) {
const newInstance = new Object(); // empty object
Object.setPrototypeOf(newInstance, myConstructor.prototype); // set prototype
const bindedConstructor = myConstructor.bind(newInstance); // this binding
bindedConstructor(name, age); // execute binded constructor function
return newInstance; // return instance
};
// 3. produce new instance
const instance = new myConstructor("jun", 28);
const instance2 = newOperatorWithConstructor("jun", 28);
console.log(instance);
console.log(instance2);
new Constructor
实现包含Object.create
方法 newOperatorWithConstructor = function(name, age) {
const newInstance = Object.create(myConstructor.prototype); // empty object, prototype chaining
const bindedConstructor = myConstructor.bind(newInstance); // this binding
bindedConstructor(name, age); // execute binded constructor function
return newInstance; // return instance
};
console.log(newOperatorWithConstructor("jun", 28));