25

只有 3 行代码,但我无法完全掌握这一点:

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

(来自原型继承

  1. Object.create()首先创建一个名为F. 我认为函数是一种对象。这个F对象存储在哪里?在全球范围内,我猜。

  2. 接下来我们oldObject传入的 aso成为函数的原型F。函数(即对象)F现在从 our 中“继承” oldObject,因为名称解析将通过它进行路由。很好,但我很好奇对象的默认原型是什么,对象?函数对象也是如此吗?

  3. 最后,F被实例化并返回,成为我们的newObject. 这里的new操作是严格必要的吗?还没有F提供我们需要的东西,或者功能对象和非功能对象之间是否存在关键区别?显然,使用这种技术不可能有一个构造函数。

下一次会发生什么Object.create()?全局函数是否F被覆盖?当然它不会被重用,因为这会改变以前配置的对象。如果多个线程调用会发生什么Object.create(),是否有任何同步来防止竞争条件F

4

4 回答 4

30

1) Object.create() 首先创建一个名为 F 的空函数。我认为函数是一种对象。这个 F 对象存储在哪里?在全球范围内,我猜。

不,它存储在函数的本地范围内Object.create,每次调用Object.create此函数F时都会重新创建。

您甚至可以通过存储F在闭包上并重用它来创建更节省内存的实现:

if (typeof Object.create !== "function") {
  Object.create = (function () {
    function F() {} // created only once
    return function (o) {
      F.prototype = o; // reused on each invocation
      return new F();
    };
  })();
}

2) 接下来我们的 oldObject,作为 o 传入,成为函数 F 的原型。函数(即对象)F 现在“继承”我们的 oldObject,在这个意义上,名称解析将通过它。很好,但我很好奇对象的默认原型是什么,对象?函数对象也是如此吗?

所有对象都有一个构建原型链的内部属性,这个属性被称为[[Prototype]],它是一个内部属性,尽管一些实现允许你访问它,比如 mozilla,使用该obj.__proto__属性。

[[Prototype]]创建新对象时的默认值,var obj = {};Object.prototype.

所有函数都有一个prototype属性,当函数用作Constructor时使用此属性,使用运算符调用new

它在幕后创建了一个新的对象实例,并且这个对象[[Prototype]]被设置为它的 Constructorprototype属性。

3)最后,F被实例化并返回,成为我们的newObject。这里的“新”操作是绝对必要的吗?F 不是已经提供了我们需要的东西,还是函数对象和非函数对象之间存在关键区别?显然,使用这种技术不可能有一个构造函数。

是的,new操作员在这种方法中是必不可少的。

操作符是设置对象内部属性的new唯一标准方法[[Prototype]],如果您对它的工作原理感到好奇,可以看一下[[Construct]]内部操作。

下次调用 Object.create() 时会发生什么?全局函数 F 是否被覆盖?当然它不会被重用,因为这会改变以前配置的对象。如果多个线程调用 Object.create() 会发生什么,是否有任何同步来防止 F 上的竞争条件?

下次Object.create调用时,只在方法调用的范围内实例化一个新的本地F函数,您不必担心竞争条件

请注意,此实现几乎不符合ECMAScript 5th Edition SpecificationObject.create中的描述,在该方法中,您可以传递属性描述符来初始化对象。

所有浏览器供应商都在实现它(已经在 Firefox 3.7 alphas、最新的 Wekit Nightly Builds 和 Chrome 5 Beta 上可用),所以我建议您至少在覆盖它之前检查是否存在本机实现。

于 2010-05-04T14:56:24.660 回答
7

1) 函数确实是一种对象。F每次调用时都会创建一个带有标识符的函数对象Object.create,并且只能在Object.create. 因此,每次Object.create调用,都会得到不同的函数对象F。这个函数对象作为constructor返回的对象的属性存在Object.create

2)

F 现在“继承”了我们的 oldObject,从某种意义上说,名称解析将通过它进行路由

这真的不正确。将对象分配给函数someObjectprototype属性只是意味着通过调用此函数作为构造函数创建的任何未来对象的原型都是someObject.

3) 这new对这项技术绝对至关重要。只有通过调用一个函数作为构造函数,它才会生成一个新对象,并且该对象的原型(通常不可访问)被设置为构造函数的prototype属性。没有其他(标准化)方法可以设置对象的原型。

最后,浏览器中的 JavaScript 是单线程的,因此您描述的竞争条件是不可能的。

于 2010-05-04T14:25:19.580 回答
2

> 显然,使用这种技术是不可能有构造函数的。

该技术已经是一个对象构造函数,因为它返回 new F(),但不能像 new man('John','Smith') 那样设置任何属性值。但是,如果修改了 Object.create 代码,则可以进行实例化。例如,下面的 sarah 对象可以使用 Object.creator 构造和实例化,并将继承 getName 方法。

var girl = {
   name: '',
   traits: {},
   getName: function(){return this.name}
}

var sarah = Object.creator(girl, 'Sarah', {age:29,weight:90})

sarah 对象将包含自己的属性 { name:'Sarah', traits:{age:9,weight:49} },继承的原型 sarah.getName() 将产生 'Sarah'。

以下方法依赖于以创建顺序使用“for(prop in o)”枚举的自己的属性。尽管 ECMA 规范不能保证,但这个示例(以及一些更复杂的示例)适用于所有测试过的主要浏览器 (4),前提是使用了 hasOwnProperty(),否则不适用。

Object.creator = function(o) {
   var makeArgs = arguments 
   function F() {
      var prop, i=1, arg, val
      for(prop in o) {
         if(!o.hasOwnProperty(prop)) continue
         val = o[prop]
         arg = makeArgs[i++]
         if(typeof arg === 'undefined') break
         this[prop] = arg
      }
   }
   F.prototype = o
   return new F()
}

官方的 ECMA Object.create 有一个可选的第二个参数,propertiesObject,可以实例化属性值,但它是一个对象而不是通常的列表,使用起来看起来很别扭。例如,我相信:-

o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });

相当于更简单的旧方法:-

o2 = new function(p) { this.p=p }(42)

o2 = Object.creator({p:''}, 42)
于 2011-07-04T00:53:27.647 回答
2

您在这里的主要误解是 F 具有全局范围。它在 Object.create 的主体中声明,因此仅在该方法块的范围内。

于 2010-05-04T14:24:25.900 回答