最近,我偶然发现了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的词法(相对于块)类型范围,这是合乎逻辑的。

以上语句正确吗?我想念什么吗?您什么时候可以使用另一个?

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

评论

另请参见使用“ Object.create”代替“ new”

new Object()和对象文字符号之间有什么区别?也与此相关,即比较new,create和{}

#1 楼


Object.create中使用的对象实际上构成了新对象的原型,而与new Function()一样,声明的属性/函数不构成原型。


是的,Object.create建立了一个对象,该对象直接继承自第一个参数传递的对象。

通过构造函数,新创建的对象继承自构造函数的原型,例如:

var o = new SomeConstructor();


在上面的示例中,o直接继承自SomeConstructor.prototype

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


无法像使用函数语法那样使用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.prototype方法,而不是Crockford的shim。

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

评论


@CMS 2个问题。 1)Object.create(null)上的作用域链是否仍终止于全局作用域(例如浏览器中的“窗口”),还是终止于自身? 2)我仍然不清楚为什么引入了Object.create(例如,缺少解决此功能的功能是什么?),以及为什么有人会用它代替new Function();

–马特
2010年11月12日下午16:30

@Matt,1)范围链实际上不是一个相关概念,范围链与标识符解析相关,例如:how foo;在当前词汇环境中得到解决。 2)为了提供一种实现继承的简便方法,这是一个非常强大的构造。 IMO我将使用它,因为它确实非常简单且轻巧,但是对于生产代码,我们仍然需要等待一段时间,直到广泛支持ES5。关于缺少的功能,创建“原始”对象的事实Object.create(null);丢失了,实现可靠的类似于哈希表的对象真的很有用...

–基督徒C.Salvadó
10-11-12在16:38

@CMS谢谢。因此,简单地使用“ Object.create”创建对象时,便能够选择应作为其原型的对象。

– Anshul
2014年9月2日于18:52

@CMS O.K.,所以Object.create(null)意味着您在迭代时不必使用hasOwnProperty()废话,因为它不会继承任何东西???我喜欢-谢谢。当然,每个人仍然会做hasOwnProperty,因为不是每个人都会使用Object.create(null),所以我不确定这是否有真正的好处...到目前为止,我已经找到了Object.create()的其他“好处”完全令人信服。

–user949300
2014年9月3日4:12



#2 楼

简单地说,new XObject.create(X.prototype),另外还运行constructor功能。 (并且给constructor一个机会,让return代替表达式this成为表达式的结果。)

就是这样。 :)

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

评论


+1简单明了! (尽管Object.create(null)似乎是一个不错的选择-也许应该提一下)。

–user949300
2014年9月3日在4:15

保持简单就是要走的路

– Bill
16年5月26日在12:12

@Qwertie:在JS中,一切都是对象。 :)他们从Java复制了该代码,然后从SmallTalk复制了该代码,直到最后。这是“新兴”的好例子,可以使生活更轻松。

– Evi1M4chine
16年6月30日在14:54

@ Evi1M4chine实际上在Java中,函数不是对象(就此而言,它们也不是基元)...并且对象没有原型,因此比较似乎不合适。 JS与其他流行的OO语言的工作方式不同的事实是造成混淆的主要原因(而且浏览器没有提供一种简便的方法来可视化包括功能和原型在内的对象网络,这也无济于事。附言我发现此链接很有帮助:davidwalsh.name/javascript-objects-deconstruction

– Qwertie
16年7月2日在4:27



@Qwertie:我并不是说Java完全遵循了这一理念。他们有哲学。他们只是半途而废。 :)但是SmallTalk当然也遵循它。 …而且OOP不等于基于类的OOP。JS是基于原型的OOP,但是它们都具有相同的OOP。实际上,与基于类的方法相比,JS的OOP哲学更干净,更优雅,更通用。他们也没有很好地实现它。 (应该使用JavaScript 2来解决所有问题,并且会非常不错。WebAssembly使得所有问题无济于事。:)

– Evi1M4chine
16-11-29在0:38



#3 楼

这是两个调用在内部执行的步骤:
(提示:唯一的区别是在步骤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不会执行构造函数。

评论


@Ray所以使用object.create字体具有构造函数中提到的函数的属性吗?

–user2156081
17年2月21日在12:28

@sortednoun,只要属性是私有的且未在原型上指定,是的,它们不会被继承,并且您也不会在新对象中使用它们(我会补充说,您可以期望获得最终的原型属性从父级开始,直到父级构造函数至少执行一次。

–卡马羽
17年7月25日在16:52

与大多数构造函数一样,方法是在返回的对象中定义的,new基本上重复了所有函数,而Object.create则没有。

– SparK
4月7日19:59

#4 楼

让我尝试解释(有关Blog的更多信息):


当您编写Car构造函数var Car = function(){}时,这是内部的情况:

我们只有一个{prototype}隐藏到无法访问的Function.prototype的链接,以及一个指向可以访问且实际prototypeCar.prototypeconstructor链接。 Function.prototype和Car.prototype都具有指向Car的隐藏链接。

当我们想使用Object.prototype运算符和new方法创建两个等效对象时,我们必须像这样进行操作:createHonda = 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中,您没有那种自由。

OO JavaScript中的首选方式:

假设我们有两个对象{prototype}new Car();

var a = new Object();
var b = new Object();
现在,假设a有一些b也想访问的方法。为此,我们需要对象继承(仅当我们要访问那些方法时,a才应该是b的原型)。如果检查ab的原型,那么我们会发现它们共享原型a

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


问题—我们希望将对象b作为Object.prototype的原型,但是在这里,我们使用原型a创建了对象b
解决方案— ECMAScript 5引入了b,可以轻松实现这种继承。如果我们这样创建对象Object.prototype

var b = Object.create(a);


然后,

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


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

评论


因此,它有点类似于没有构造函数调用的对象创建?我们将享受全班的所有好处。 obj instanceof类也将为true。但是我们不是通过new调用Class函数。

–普拉文
16 Mar 30 '16 at 8:49

@Anshul您说过a.isPrototypeOf(b);将返回false,这是正确的,因为两个对象都不同并且指向不同的内存。使用new运算符执行此操作的正确方法在这里。 -jsfiddle.net/167onunp。

– Sagar Karira
16-11-10在11:29



为什么不将b的prototype属性设置为a,而不是这样做呢?

–记忆删除
16 Dec 3'在19:09

也喜欢您博客上的文章。帮助我更好地理解了这个概念。谢谢。

–steady_daddy
17年8月9日在9:59

结论说明了一切。

– HalfWebDev
17年8月14日在11:20

#5 楼

这:

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,您可以在利用原型继承的同时从代码中省略构造函数。

– Ricky Boyce
2015年11月19日在4:38



#6 楼

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

在伪经典继承(使用“ new”运算符)中,假设您先定义一个伪类,然后创建对象从那个班级。例如,定义一个伪类“ Person”,然后从“ Person”创建“ Alice”和“ Bob”。

在原型继承中(使用Object.create),您可以直接创建一个特定的类。人“爱丽丝”,然后使用“爱丽丝”作为原型创建另一个人“鲍勃”。这里没有“班级”;所有都是对象。

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

有关两种方式的比较,请参见此链接。

#7 楼

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对此进行引用)

#8 楼

对象创建变体。


变量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





变量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 }





Variant 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!





Variant 3.2:“对象”。 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!




评论


不错的总结。谢谢。今天对我有帮助!

– Anandaraja_Srinivasan
19/12/12在8:49

===运算符在这里很复杂,因为我不理解您的第二个解释。 ==会是笨蛋,但===。

–snr
9月1日13:19

#9 楼

Object.create在内部执行此操作:

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


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

评论


ECMAScript 5 Object.create方法的功能远不止于此,您可以通过属性描述符定义属性,还可以创建一个不继承任何对象的对象(Object.create(null);),这种垫片应该避免使用,因为您无法真正在ES3上模仿这种行为。更多信息

–基督徒C.Salvadó
10-11-12在16:24



同意@CMS,但通常来说,它是Object.create的简单polyfill。

–cn007b
17年7月27日在21:02

#10 楼

相应于此答案和此视频,new关键字还会执行以下操作:


创建新对象。
将新对象链接到构造函数(prototype)。
使this变量指向新对象。
使用新对象执行构造函数并隐式执行return this;
将构造函数名称分配给新对象的属性constructor

Object.create仅执行1st2nd步骤!!!

#11 楼

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));
 


评论


不要使用新对象,而要使用对象文字。

–贝尔吉
9月27日18:53

不要使用.bind(x)(... args),而只需使用.call(x,... args)。

–贝尔吉
9月27日18:53