理解 Object.create() 和 new SomeFunction() 的区别

IT技术 javascript prototype object-create
2021-01-11 18:30:24

我最近偶然发现了Object.create()JavaScript 中方法,并试图推断它与使用 来创建对象的新实例有何不同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/

6个回答

很简单的说,new X就是Object.create(X.prototype)带有额外的运行constructor功能。(并为实际对象提供constructor机会,return该对象应该是表达式的结果而不是this。)

而已。:)

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

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

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。

该方法开始在最新浏览器上本地实现,请查看此兼容性表

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

以下是两个调用在内部发生的步骤:(
提示:唯一的区别在于第 3 步)


new Test()

  1. 创建new Object()对象
  2. 设置obj.__proto__Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. 创建new Object()对象
  2. 设置obj.__proto__Test.prototype
  3. return obj;

所以基本上Object.create不执行构造函数。

@sortednoun 只要属性是私有的并且未在原型上指定,是的,它们不会被继承,并且您不会在新对象中拥有它们(而且,我要补充的是,您可以期望获得最终的原型属性从父级开始,就在父级构造函数至少执行一次时)。
2021-03-21 18:30:24
与大多数构造函数一样,方法是在返回的对象中定义的,new基本上所有的函数都是重复的,而Object.create没有。
2021-03-26 18:30:24
@Ray 所以使用 object.create 我们字体具有构造函数中提到的函数的属性?
2021-04-06 18:30:24

让我试着解释一下(更多关于博客):

  1. 当您编写Car构造函数时var Car = function(){},内部情况是这样的: 创建javascript对象时的原型链图 我们有一个无法访问的{prototype}隐藏链接Function.prototype和一个可访问的prototype链接,Car.prototype并且具有实际constructorCar. Function.prototype 和 Car.prototype 都有指向Object.prototype.
  2. 当我们想使用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)— 当您创建这样的对象时,您有一个额外的选项来选择对象的{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).

问题——我们想要 objecta作为 的原型b,但在这里我们b用原型创建了 object Object.prototype解决方案——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()对于继承非常有用。

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

这:

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()then的两个参数版本,您可以做更强大的事情。

很好的解释。我可以补充一下,Object.create像这样以最简单的形式使用允许您在利用原型继承的同时从代码中省略构造函数。
2021-03-12 18:30:24