Crockford的对象创建技术发生了什么?

时间:2010-05-04 14:18:10

标签: javascript object prototypal-inheritance creation

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

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

(来自Prototypal Inheritance

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

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

  3. 最后,F被实例化并返回,成为我们的newObjectnew操作在这里是否必要? F是否已经提供了我们需要的东西,或者函数对象和非函数对象之间是否存在重要区别?显然,使用这种技术不可能有构造函数。

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

4 个答案:

答案 0 :(得分:29)

  

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]],它是一个内部属性,虽然有些实现允许你使用obj.__proto__访问它,比如mozilla属性。

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

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

在幕后创建的新对象实例,此对象[[Prototype]]设置为其构造函数的prototype属性。

  

3)最后,F被实例化   返回,成为我们的newObject。是   “新”行动是绝对必要的   这里?不是F已经提供了什么   我们需要,或者是否有批评   功能对象之间的区别   和非功能对象?显然它   是不可能的   使用此构造函数   技术

是的,new运算符在此方法中至关重要。

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

  

下一次会发生什么   是否调用了Object.create()?是全球的   功能F被覆盖了吗?当然是   没有重复使用,因为那会改变   先前配置的对象。和   如果多个线程调用会发生什么   Object.create(),有没有   同步以防止种族   F?

的条件

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

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

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

答案 1 :(得分:7)

1)函数确实是一种对象。每次调用F时都会创建一个标识为Object.create的函数对象,并且只能在Object.create的执行中使用该标识符访问该函数对象。因此,每次调用Object.create时,您都会获得不同的函数对象F。此函数对象作为constructor返回的对象的Object.create属性而存在。

2)

  

F现在从我们的oldObject“继承”,   从名称解析的意义上说   通过它途径

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

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:2)

  

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

该技术已经是一个对象构造函数,因为它返回了新的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)