__proto__,什么时候会消失?备择方案?

IT技术 javascript prototype-programming
2021-02-19 09:34:01

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

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

__proto__ 允许简单的继承:

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

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

谢谢

3个回答

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

更新:浏览器不会删除该__proto__属性。事实上,ECMAScript Harmony现在已经标准化了__proto__属性和setPrototypeOf功能。__proto__仅出于遗留原因支持属性。强烈建议您使用setPrototypeOfgetPrototypeOf而不是__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__属性)。

你实际上是在告诉人们编写损坏的代码,违背标准机构和 JS 引擎实现者的明确建议。Windows API 和内部结构之所以变得如此混乱,就是因为这样的错误。如果你可以避免它,你真的不应该。我真的不认为你应该在没有任何警告的情况下推荐它。你和我都不是比规范或语言的实现者更高的权威。请记住:实际上并没有包含 JavaScript 的版本__proto__——它是某些实现碰巧暴露的内部细节。
2021-04-29 09:34:01
@Chuck,__proto__正如@cliffsofinsanity 上面指出的那样,该属性被认为是下一版 JavaScript 的标准。实施者建议不要使用它,因为它还不是标准。因此它可能不适用于所有 JavaScript 引擎。理解为什么不建议使用某个功能而不是仅仅批评人总是一个好主意。__proto__在网络上使用该属性完全没问题,因为所有主要浏览器都支持它。我没有看到使用它的任何警告,除非它在 ​​Rhino 中不起作用。它与 Windows 无关。
2021-05-05 09:34:01
@AaditMShah:非常感谢!不知道你为什么称之为setPrototypeOf“不安全”?顺便说一句,似乎 ES6 标准化了Reflect.setPrototypeOf,而所有浏览器都支持Object.setPrototypeOf. 我看到即将到来的进一步混乱:-)
2021-05-14 09:34:01
@Bergi 我通过删除许多过时的信息并添加 ES6 提案的结果来更新我的答案。
2021-05-18 09:34:01
@BenjaminGruenbaum - 我同意。事实上,我已经提出了一个解决方案来解决这个问题。不幸的是,它置若罔闻。麻烦您读一下并告诉我您的想法吗?我相信我的实例化函数对于 JavaScript 程序员来说是一个强大的资产,因为它不使用__proto__它是安全的。
2021-05-19 09:34:01

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

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

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

在不支持它的浏览器中也有 shim 来模仿这个功能,如果你需要使用它的话,这是另一个优于直接使用__proto__.

@AaditMShah:他的描述是,与“功能继承”相反,他“只想[s] 创建一个原型链”。这表明他想要纯原型继承,这就是 Object.create 的用途。动态处理原型很有用,但不是通常需要的。
2021-04-21 09:34:01
我认为 OP 不想用给定的原型创建一个新对象。我认为他想在不修改__proto__.
2021-04-24 09:34:01
此外,通过重新分配原型来改变对象要慢得多并且不鼓励。Object.create 是更快的方式。
2021-05-13 09:34:01

我不明白如何:

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__

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

@Drew 首先,第一个不使用经典继承。其次,需要更多代码的只是一次性设置成本,对象的实际创建不仅代码更少,而且允许初始化逻辑,而无需创建单独的工厂。请记住,JS 中的构造函数像任何其他函数一样运行,它不像经典语言中的构造函数那样受到限制,因此在这些语言中创建工厂的许多理由不适用于 js。
2021-04-30 09:34:01
对于 init 逻辑,可以使用工厂而不是依赖构造函数。您需要更深入地挖掘才能看到value。如果你的两个版本都有原型,Object.create 会简单一些。请参阅:函数构造函数(){}; Constructor.prototype = { /*methods*/ }; var newObject = new Constructor(); VS var proto = { /*方法*/ }; var newObject = Object.create(proto);。我刚刚用比定义我的经典继承更少的代码创建了一个新的原型对象。
2021-05-15 09:34:01