ChatGPT解决这个技术问题 Extra ChatGPT

了解 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;控制台.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/


u
user1063287

很简单地说,new X 就是 Object.create(X.prototype),另外还运行了 constructor 函数。 (并让 constructor 有机会 return 应该是表达式结果的实际对象,而不是 this。)

而已。 :)

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


+1 简单明了! (虽然 Object.create(null) 似乎是一个不错的选择 - 也许应该提到这一点)。
保持简单这是要走的路
@Qwertie:在 JS 中,一切都是对象。 :) 他们从 Java 中复制了它,从 SmallTalk 中复制了它,后者一直使用它。这是一个很好的“出现”案例,总体上让生活更轻松。
@Evi1M4chine 实际上在 Java 中,函数不是对象(就此而言,原语也不是)......而且对象没有原型,所以比较似乎不合适。 JS 与其他流行的 OO 语言的工作方式不同这一事实是造成混淆的主要原因(浏览器没有提供一种简单的方法来可视化对象网络,包括函数和原型,这无济于事)。 PS 我发现这个链接很有帮助:davidwalsh.name/javascript-objects-deconstruction
@Qwertie:我并没有说 Java 完全遵循了这一理念。他们有哲学。他们只是半途而废。 :) 但是 SmallTalk 肯定也跟着它。 ......而且OOP不等于基于类的OOP。JS是基于原型的OOP,但它们都有OOP的共同点。事实上,JS 的 OOP 哲学比基于类的方法更简洁、更优雅、更通用。他们也未能很好地实现它。 (JavaScript 2 应该可以解决所有这些问题,并且会非常好。WebAssembly 让这一切变得毫无意义。:)
j
jithinkmatthew

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


@CMS 2 个问题。 1) Object.create(null) 上的作用域链是否仍终止于全局作用域(例如浏览器中的“窗口”),还是终止于自身? 2) 我仍然不清楚为什么要引入 Object.create(例如,这解决了哪些功能缺失?)以及为什么要使用它而不是 new Function();
@Matt, 1) 作用域链在这里并不是一个真正的相关概念,作用域链与标识符解析有关,例如:如何在当前词法环境foo; >。 2)为了提供一种简单的方法来实现继承,它是一个非常强大的构造。 IMO 我会使用它,因为它非常简单和轻量级,但是对于生产代码,我们仍然需要等待一段时间,直到 ES5 得到广泛支持。关于缺少的功能,创建“原始”对象的事实,Object.create(null); 丢失了,实现可靠的类似哈希表的对象真的很有用......
@CMS 谢谢。因此,当您使用“Object.create”创建对象时,您就可以选择应该作为其原型的对象。
@CMS 好的,所以 Object.create(null) 意味着您在迭代时不必使用 hasOwnProperty() 废话,因为它没有继承???我喜欢 - 谢谢。当然,每个人仍然会使用 hasOwnProperty,因为不是每个人都会使用 Object.create(null),所以我不确定它是否有真正的好处......到目前为止,我已经找到了其他“好处” Object.create() 完全没有说服力。
R
Ray Hulha

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


@Ray 所以使用 object.create 我们的字体具有构造函数中提到的函数属性?
@sortednoun 只要属性是私有的并且未在原型上指定,是的,它们不会被继承,并且您不会将它们包含在新对象中(并且,我要补充一点,您可以期望获得最终的原型属性来自父级,就在父级构造函数至少执行一次时)。
与大多数构造函数一样,方法是在返回的对象中定义的,new 基本上所有函数都是重复的,而 Object.create 没有。
这对我来说是一个重要的区别。如果我的原型有带值的道具并且我使用 Object.create() 然后尝试说获取对象上这些道具的值,我会得到 undefined 而由于 new() 运行构造函数,我可以立即查询道具值.
l
limeandcoconut

让我试着解释一下(更多关于 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 中的首选方式:

假设我们有两个对象 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).

问题 - 我们希望对象 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() 对于继承非常有用。


那么,它有点类似于没有构造函数调用的对象创建?我们将享受课程的所有好处。 obj instanceof Class 也将为真。但是我们不是通过 new 调用 Class 函数。
@Anshul您说 a.isPrototypeOf(b); 将返回 false 这是正确的,因为两个对象不同并且指向不同的内存。使用 new 运算符执行此操作的正确方法在这里。 - jsfiddle.net/167onunp
为什么不直接将 b 的原型属性设置为 a,而不是这样做呢?
也喜欢你博客上的文章。帮助我更好地理解了这个概念。谢谢你。
结论说明了一切。
L
Leopd

这个:

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

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

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

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

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

有关这两种方式的比较,请参见 this link


P
Pedro del Sol
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 )


T
Ted

对象创建变体。

变体 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 但 ===
x
xj9

Object.create 在内部这样做:

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

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


ECMAScript 5 Object.create 方法的功能远不止这些,您可以通过 属性描述符 定义属性,并且可以创建一个不继承自任何东西 (Object.create(null);) 的对象,这种类型应该避免使用 shims,因为你无法在 ES3 上真正模拟这种行为。 More info
同意@CMS,但总的来说,它是 Object.create 的简单 polyfill。
c
cn007b

根据 this answerthis video new 关键字执行以下操作:

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

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


J
Jun

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)
@Bergi .apply(x, args) ;-)