16

Mozilla 声称它会在不久前(~2008 年)删除 __proto__ 并且它仍然在浏览器中。它还会被弃用吗?它也适用于 Opera(我认为是 Safari)和 Chrome。我不需要担心 IE,所以我很想继续使用它。

但是,我不希望我的代码有一天停止工作,所以我的问题是:

__proto__ 允许简单的继承:

a.__proto__ = {'a':'test'}

无论如何我可以以符合标准的方式复制它吗?我知道有函数继承,这很丑陋,而且它使我只想创建一个原型链的事实过于复杂。只是想知道是否有任何巫师解决了这个问题。

谢谢

4

3 回答 3

27

注意:更改__proto__. JavaScript 的创建者 Brendan Eich 等人强烈反对这样做。事实上,该__proto__属性已从一些 JavaScript 引擎(如 Rhino)中完全删除。如果您想知道原因,请阅读 Brendan Eich 的以下评论

更新:浏览器不会删除该__proto__属性。事实上,ECMAScript Harmony现在已经标准化了__proto__属性和setPrototypeOf函数。__proto__仅出于遗留原因支持该属性。强烈建议您使用setPrototypeOfandgetPrototypeOf而不是__proto__.

警告:虽然setPrototypeOf现在是一个标准,但您仍然不鼓励使用它,因为改变对象的原型总是会破坏优化并使您的代码变慢。此外,使用setPrototypeOf通常表示代码质量差。


你不必担心你现有的代码有一天会失效。该__proto__物业将留在这里。

现在,对于手头的问题。我们希望以符合标准的方式做类似的事情:

var a = {
    b: "ok"
};

a.__proto__ = {
    a: "test"
};

alert(a.a); // alerts test
alert(a.b); // alerts ok

显然你不能用它Object.create来达到这个目的,因为我们没有创建一个新对象。我们只是试图改变[[proto]]给定对象的内部属性。[[proto]]问题是一旦创建对象就不可能改变它的内部属性(除了__proto__我们试图避免的使用)。

所以为了解决这个问题,我写了一个简单的函数(注意它适用于除函数之外的所有对象):

function setPrototypeOf(obj, proto) {
    var result  = Object.create(proto);
    var names   = Object.getOwnPropertyNames(obj);
    var getProp = Object.getOwnPropertyDescriptor;
    var setProp = Object.defineProperty;
    var length  = names.length;
    var index   = 0;

    while (index < length) {
        var name = names[index++];
        setProp(result, name, getProp(obj, name));
    }

    return result;
}

因此,我们现在可以在创建任何对象后更改其原型,如下所示(请注意,我们实际上并没有更改[[proto]]对象的内部属性,而是创建一个与给定对象具有相同属性并继承自给定原型的新对象):

var a = {
    b: "ok"
};

a = setPrototypeOf(a, {
    a: "test"
});

alert(a.a); // alerts test
alert(a.b); // alerts ok
<script>
function setPrototypeOf(obj, proto) {
    var result  = Object.create(proto);
    var names   = Object.getOwnPropertyNames(obj);
    var getProp = Object.getOwnPropertyDescriptor;
    var setProp = Object.defineProperty;
    var length  = names.length;
    var index   = 0;

    while (index < length) {
        var name = names[index++];
        setProp(result, name, getProp(obj, name));
    }

    return result;
}
</script>

简单高效(我们没有使用该__proto__属性)。

于 2012-05-07T05:46:00.443 回答
7

我认为 Mozilla 想要提出的实际观点是它是非标准的,因此实施者完全有权删除它。

更简洁的原型链方式是Object.create. a使用原型创建对象的代码的等价物{'a': 'test'}是:

a = Object.create({'a':'test'})

如果您需要使用一个,在不支持它的浏览器中也可以使用 shims 来模拟此功能,这是与直接使用__proto__.

于 2012-05-07T04:39:00.957 回答
1

我不明白如何:

var a = {};
a.__proto__ = A; // A is object because no constructor is needed

比简单:

var a = new A(); // A is Constructor function

如果您不需要构造函数,现在在后一种情况下设置 A 可能会更冗长,但在这种情况下,您可以将其自动化,使其变得同样简单:

var A = Constructor({
    method: function(){}
}); // A is function, meant to be used with new

相比:

var A = {
    method: function(){}
}; //A is object, meant to be set as __proto__

而且,如果您需要一些初始化逻辑,那么使用通常需要声明构造函数的方式最终可能会更容易

于 2012-05-07T04:25:46.080 回答