1

我对以下规范的 object.create 方法有疑问:

Object.create = function(o, props) {
    function F() {}
    F.prototype = o;

    if (typeof(props) === "object") {
     for (prop in props) {
      if (props.hasOwnProperty((prop))) {
       F[prop] = props[prop];
      }
     }
    }
    return new F();
   };

在上述代码的第 3 行,我们将 F 对象的原型属性设置为 o 参数的原型。

我会认为这意味着 o 和 F 都指向同一个原型,因此指向同一个成员集。

但是代码随后会在 props 循环中复制 prop 中的所有成员。

如果我们手动复制所有成员,那么在第 3 行设置原型有什么意义?

4

3 回答 3

1

您提供的代码等同于Object.create()ECMA 标准定义的代码。第二个参数的成员,这里称为prop,应该是要在结果对象上定义的一组描述符,而不是复制到构造函数。这更准确(但也不完全正确),

Object.create = function(o, props) { 
    function F() {} 
    F.prototype = o; 
    var result = new F();
    if (typeof(props) === "object") 
       for (prop in props)
          if (props.hasOwnProperty(prop) && typeof(props[prop].value) != "undefined")
             result[prop] = props[prop].value; 
    return result;
}; 

props参数包含您希望与原型中指定的新对象不同的成员的属性描述符o。这些值不应该按字面意思复制到对象中,而是应该像调用 一样进行处理,上面通过将描述符的一部分Object.defineProperties()分配给新对象来模拟。不能准确地填充到 ECMA 3 中,因为它可以用于指定具有访问器函数的属性,尽管上述方法适用于使用而不是or的情况。valueObject.create()propsvaluegetset

如果您对 polyfill 版本不感兴趣,以下是对功能的准确描述Object.create(),假设同时存在__proto__Object.defineProperties()

Object.create = function (o, props) {
    if (typeof(o) !== "object" && o != null) throw new TypeError();
    var result = new Object();
    if (o != null)
        result.__proto__ = o;
    if (typeof(props) !== "undefined")
        Object.defineProperties(result, props);
    return result;
};
于 2012-09-06T08:46:16.390 回答
1

您的问题的版本有一个错误Object.create:循环将属性附加到构造函数F(而不是返回的对象或其原​​型),这意味着它们在创建的对象中不可访问。

第二个参数 to 的属性Object.create应该被复制到新创建的对象中。MozillaObject.create的文档是这样写的:

如果指定且未定义,则其可枚举自身属性的对象(即,在其自身上定义的那些属性,而不是沿其原型链的可枚举属性)指定要添加到新创建的对象的属性描述符,以及相应的属性名称。

尝试使用问题中的版本运行以下代码Object.create

o = Object.create(
        {a: "prototype's a", b: "prototype's b"},
        {a: "object's a"}
    );

你会发现o.a == "prototype's a"and丢失了。o.b == "prototype's b""object's a"

该函数的以下版本可能会更有用:

Object.create = function(o, props) {
    var newObj;

    // Create a constructor function, using o as the prototype
    function F() {}
    F.prototype = o;

    // Create a new object using F as the constructor function
    newObj = new F();

    // Attach the properties of props to the new object
    if (typeof(props) === "object") {
        for (prop in props) {
            if (props.hasOwnProperty((prop))) {
                newObj[prop] = props[prop];
            }
        }
    }

    return newObj;
};

让我们用同样的例子来试试:

o = Object.create(
        {a: "prototype's a", b: "prototype's b"},
        {a: "object's a"}
    );

新对象o是使用具有属性和它自己的属性的原型a创建ba

我们o.b先来看:o.hasOwnProperty("b")会返回false,因为o没有一个属性叫b. 这就是原型的用武之地;因为没有属性b,所以在原型上查找它,因此o.b === "prototype's b".

另一方面,o.hasOwnProperty("a")将返回true,因为o确实有一个a属性。 o.a == "object's a"并且没有从原型中查找任何内容。

正如@chuckj 的回答所指出的,正确的实现Object.create比这更复杂。有关更多详细信息,请参阅John Resig 关于 ECMAScript 5 对象和属性的帖子

于 2012-09-06T08:48:47.023 回答
0

可以将F其视为一个函数,该函数返回对象的新实例以及F.prototype对新实例从中借用属性的对象的引用。

使用创建的实例F具有自己的属性集,独立于F.prototype.

当一个属性无法在 incance 中找到F时,运行时将加强原型链并查看该属性是否存在于F.prototype.

这就是为什么您要复制一些并继承其他的原因。

Prototype 的重点是提供类类代码重用/属性存储。

将其视为对象F.prototype的类实例F

于 2012-09-06T08:18:11.743 回答