我最近偶然发现了 JavaScript 中的 Object.create()
方法,并试图推断它与使用 new SomeFunction()
创建对象的新实例有何不同,以及何时需要使用其中一个。
考虑以下示例:
var test = { val: 1, func: function() { return this.val; } }; var testA = Object.create(test); testA.val = 2;控制台.log(test.func()); // 1 console.log(testA.func()); // 2 console.log('其他测试'); var otherTest = function() { this.val = 1; this.func = function() { return this.val; }; }; var otherTestA = new otherTest(); var otherTestB = 新的 otherTest();其他TestB.val = 2;控制台.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/
new Object()
and object literal notation? 也是相关的,比较 new、create 和 just {}
很简单地说,new X
就是 Object.create(X.prototype)
,另外还运行了 constructor
函数。 (并让 constructor
有机会 return
应该是表达式结果的实际对象,而不是 this
。)
而已。 :)
其余的答案只是令人困惑,因为显然没有其他人阅读 new 的定义。 ;)
Object.create 中使用的对象实际上形成了新对象的原型,而在 new 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 的 shim。
该方法开始在最新的浏览器上本地实现,请检查此 compatibility table。
Object.create(null);
丢失了,实现可靠的类似哈希表的对象真的很有用......
Object.create(null)
意味着您在迭代时不必使用 hasOwnProperty()
废话,因为它没有继承???我喜欢 - 谢谢。当然,每个人仍然会使用 hasOwnProperty
,因为不是每个人都会使用 Object.create(null)
,所以我不确定它是否有真正的好处......到目前为止,我已经找到了其他“好处” Object.create()
完全没有说服力。
以下是两个调用在内部发生的步骤:(提示:唯一的区别在于第 3 步)
new Test()
:
create new Object() obj set obj.__proto__ to Test.prototype return Test.call(obj) ||对象; // 通常返回 obj 但 JS 中的构造函数可以返回一个值
Object.create( Test.prototype )
create new Object() obj set obj.__proto__ to Test.prototype return obj;
所以基本上 Object.create
不执行构造函数。
new
基本上所有函数都是重复的,而 Object.create
没有。
Object.create()
然后尝试说获取对象上这些道具的值,我会得到 undefined
而由于 new()
运行构造函数,我可以立即查询道具值.
让我试着解释一下(更多关于 Blog):
当您编写 Car 构造函数 var Car = function(){} 时,内部情况是这样的:我们有一个 {prototype} 到 Function.prototype 的隐藏链接,它是不可访问的,还有一个原型链接到 Car.prototype,它是可访问的并且具有Car 的实际构造函数。 Function.prototype 和 Car.prototype 都有指向 Object.prototype 的隐藏链接。当我们想使用 new 运算符和 create 方法创建两个等效对象时,我们必须这样做: Honda = new Car();和 Maruti = Object.create(Car.prototype)。怎么了?本田 = 新车(); — 当您创建这样的对象时,隐藏的 {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
也想访问的方法。为此,我们需要对象继承(只有当我们想要访问这些方法时,a
才应该是 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()
对于继承非常有用。
a.isPrototypeOf(b);
将返回 false
这是正确的,因为两个对象不同并且指向不同的内存。使用 new
运算符执行此操作的正确方法在这里。 - jsfiddle.net/167onunp。
这个:
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()
的两参数版本,那么您可以做更强大的事情。
Object.create
可以让您在代码中省略构造函数,同时利用原型继承。
区别在于所谓的“伪经典与原型继承”。建议在您的代码中仅使用一种类型,而不是混合使用这两种类型。
在伪经典继承(使用“new”运算符)中,假设您首先定义一个伪类,然后从该类创建对象。例如,定义一个伪类“Person”,然后从“Person”创建“Alice”和“Bob”。
在原型继承中(使用 Object.create),您直接创建一个特定的人“Alice”,然后使用“Alice”作为原型创建另一个人“Bob”。这里没有“类”;都是对象。
在内部,JavaScript 使用“原型继承”; “伪经典”方式只是一些糖。
有关这两种方式的比较,请参见 this link。
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 )
对象创建变体。
变体 1:'new Object()' -> 不带参数的对象构造函数。
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
https://i.stack.imgur.com/zcyc8.jpg
变体 2:'new Object(person)' -> 带参数的对象构造函数。
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 }
https://i.stack.imgur.com/UMtro.jpg
变体 3.1:'Object.create(person)'。将 Object.create 与简单对象“人”一起使用。 'Object.create(person)' 将创建(并返回)新的空对象并将属性 '__proto__' 添加到同一个新的空对象。此属性“__proto__”将指向对象“人”。
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!
https://i.stack.imgur.com/JJzaT.jpg
变体 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
https://i.stack.imgur.com/glM5q.jpg
变体 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!
https://i.stack.imgur.com/y21Xj.jpg
===
运算符在这里很复杂,因为我不明白它在您的第二个解释中是如何工作的。 ==
将是 hunky-dory 但 ===
。
Object.create
在内部这样做:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
语法只是消除了 JavaScript 使用经典继承的错觉。
Object.create
方法的功能远不止这些,您可以通过 属性描述符 定义属性,并且可以创建一个不继承自任何东西 (Object.create(null);
) 的对象,这种类型应该避免使用 shims,因为你无法在 ES3 上真正模拟这种行为。 More info
Object.create
的简单 polyfill。
根据 this answer 和 this video new
关键字执行以下操作:
创建新对象。将新对象链接到构造函数(原型)。使该变量指向新对象。使用新对象执行构造函数并隐式执行 return this;将构造函数名称分配给新对象的属性构造函数。
Object.create
只执行 1st
和 2nd
步骤!!!
Object.create(Constructor.prototype)
是 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);
新的构造函数实现包含 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));
new Object
,使用对象文字。
.bind(x)(...args)
,只使用 .call(x, ...args)
。
.apply(x, args)
;-)