如何设置已经实例化的 JavaScript 对象的原型?

IT技术 javascript function-prototypes
2021-01-17 13:50:44

假设我的fooJavaScript 代码中有一个对象foo是一个复杂的对象,它是在其他地方生成的。如何更改foo对象的原型

我的动机是为从 .NET 序列化为 JavaScript 文字的对象设置适当的原型。

假设我在 ASP.NET 页面中编写了以下 JavaScript 代码。

var foo = <%=MyData %>;

假设这MyData是在对象JavaScriptSerializer调用 .NET 的结果Dictionary<string,string>

在运行时,这变成了以下内容:

var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];

如您所见,foo变成了一个对象数组。我希望能够foo使用适当的原型进行初始化不是要修改Object.prototype,也没有Array.prototype我怎样才能做到这一点?

6个回答
更新: ES6 现在指定Object.setPrototypeOf(object,prototype)

.

2012 年 2 月编辑:下面的答案不再准确。proto被添加到 ECMAScript 6 作为“规范可选”,这意味着它不需要被实现,但如果它被实现,它必须遵循给定的规则集。这目前尚未解决,但至少它将正式成为 JavaScript 规范的一部分。

这个问题比表面上看起来要复杂得多,而且超出了大多数人对 Javascript 内部知识的了解。

prototype创建该对象的新的子对象时,对象的属性使用。改变它并不反映在对象本身,而是反映在该对象被用作其他对象的构造函数时,并且对改变现有对象的原型没有用处。

function myFactory(){};
myFactory.prototype = someOtherObject;

var newChild = new myFactory;
newChild.__proto__ === myFactory.prototype === someOtherObject; //true

对象具有指向当前原型的内部 [[prototype]] 属性。它的工作方式是,无论何时调用对象上的属性,它都会从该对象开始,然后向上遍历 [[prototype]] 链,直到在根对象原型之后找到匹配项或失败。这就是 Javascript 允许运行时构建和修改对象的方式;它有一个寻找它需要的东西的计划。

__proto__属性存在于某些实现中(现在很多):任何 Mozilla 实现,我所知道的所有 webkit 实现,以及其他一些实现。此属性指向内部 [[prototype]] 属性,并允许在创建后修改对象。由于这种链式查找,任何属性和函数都会立即切换到匹配原型。

此功能虽然现在已标准化,但仍然不是 JavaScript 的必需部分,并且在支持它的语言中,很有可能将您的代码归入“未优化”类别。JS 引擎必须尽最大努力对代码进行分类,尤其是经常访问的“热”代码,如果你在做一些花哨的事情,比如修改__proto__,他们根本不会优化你的代码。

这篇文章https://bugzilla.mozilla.org/show_bug.cgi?id=607863专门讨论了它们的当前实现__proto__和它们之间的差异。每个实现的做法都不一样,因为这是一个难以解决的问题。Javascript 中的一切都是可变的,除了 a.) 语法 b.) 宿主对象(DOM 技术上存在于 Javascript 之外)和 c.) __proto__其余的完全掌握在您和其他所有开发人员的手中,因此您可以明白为什么会__proto__像拇指酸痛一样伸出来。

有一件事__proto__允许这样做,否则是不可能做到的:在运行时指定对象原型与其构造函数分开。这是一个重要的用例,也是__proto__尚未消亡的主要原因之一重要的是,它已经成为 Harmony 制定中的一个严肃讨论点,或者很快就会被称为 ECMAScript 6。在创建过程中指定对象原型的能力将成为下一版 Javascript 的一部分,这将是指示__proto__天数的钟是正式编号的。

在短期内,__proto__如果您的目标是支持它的浏览器(不是 IE,而且 IE 永远不会),则可以使用由于 ES6 直到 2013 年才会最终确定,因此它很可能会在未来 10 年在 webkit 和 moz 中运行。

Brendan Eich - re:ES5 中新对象方法的方法

抱歉,...但是 settable __proto__,除了对象初始化器用例(即,在一个新的对象还不能到达,类似于 ES5 的 Object.create 上),是一个糟糕的主意。我写这篇文章是__proto__在 12 年前设计和实现了可设置的

...缺乏分层是一个问题(考虑带有键的 JSON 数据"__proto__")。更糟糕的是,可变性意味着实现必须检查循环原型链以避免 ilooping。[需要不断检查无限递归]

最后,__proto__对现有对象进行变异可能会破坏新原型对象中的非泛型方法,这不可能在__proto__正在设置的接收者(直接)对象上工作一般来说,这只是一种糟糕的做法,是一种有意的类型混淆。

优秀的分解!虽然它与可变原型并不完全相同,但 ECMA Harmony 可能会实现代理,它允许您使用包罗万象的模式将附加功能添加到特定对象上。
2021-03-16 13:50:44
如果不是使用魔法属性而是使用 Object.setPrototype 来完成它会更好,从而消除“__proto__在 JSON”中的问题。Brendan Eich 的其他担忧是可笑的。递归原型链或使用给定对象的方法不充分的原型是程序员错误,而不是语言错误,不应成为一个因素,此外,它们可能发生在 Object.create 和 free settable 中__proto__
2021-03-20 13:50:44
突变__proto__是需要的,因为的Object.create只会产生对象,而不是函数例如,也没有符号,的正则表达式,DOM元素或其他宿主对象。如果您希望您的对象可调用,或以其他方式特殊,但仍要更改其原型链,那么您将无法设置可设置__proto__或代理
2021-03-21 13:50:44
IE 10 支持__proto__.
2021-03-23 13:50:44
Brendan Eich 的问题是整个原型语言的固有问题。可能使 __proto__non-writable, configurable可以通过强制用户显式重新配置属性来解决这些问题。最后,不良实践与语言功能的滥用有关,而不是功能本身。可写的 __proto__ 并非闻所未闻。还有许多其他不可枚举的可写属性,虽然存在危险,但也有最佳实践。锤头不应该被移除,因为它可能会被不当使用来伤人。
2021-04-01 13:50:44

ES6 最终指定了Object.setPrototypeOf(object,prototype)已经在 Chrome 和 Firefox 中实现。

您可以constructor在对象的实例上使用来就地更改对象的原型。我相信这就是你要求做的。

这意味着如果你有foowhich 是一个实例Foo

function Foo() {}

var foo = new Foo();

您可以通过执行以下操作bar向所有实例添加属性Foo

foo.constructor.prototype.bar = "bar";

这是一个显示概念验证的小提琴:http : //jsfiddle.net/C2cpw/不太确定使用这种方法的旧浏览器会如何,但我很确定这应该可以很好地完成这项工作。

如果您打算将功能混合到对象中,则此代码段应该可以完成这项工作:

function mix() {
  var mixins = arguments,
      i = 0, len = mixins.length;

  return {
    into: function (target) {
      var mixin, key;

      if (target == null) {
        throw new TypeError("Cannot mix into null or undefined values.");
      }

      for (; i < len; i += 1) {
        mixin = mixins[i];
        for (key in mixin) {
          target[key] = mixin[key];
        }

        // Take care of IE clobbering `toString` and `valueOf`
        if (mixin && mixin.toString !== Object.prototype.toString) {
          target.toString = mixin.toString;
        } else if (mixin && mixin.valueOf !== Object.prototype.valueOf) {
          target.valueOf = mixin.valueOf;
        }
      }
      return target;
    }
  };
};
你也可以使用 foo.__proto__.bar = 'bar';
2021-03-23 13:50:44
+1:这信息丰富且有趣,但并不能真正帮助我得到我想要的东西。我正在更新我的问题以使其更具体。
2021-03-29 13:50:44

您可以执行foo.__proto__ = FooClass.prototypeFirefox、Chrome 和 Safari 支持的 AFAIK。请记住,该__proto__属性是非标准的,可能会在某个时候消失。

文档:https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto另请参阅http://www.mail-archive.com/jsmentors@googlegroups.com/msg00392.html以了解为什么没有Object.setPrototypeOf()以及为什么__proto__被弃用的解释

您可以定义代理构造函数,然后创建一个新实例并将原始对象的所有属性复制到它。

// your original object
var obj = { 'foo': true };

// your constructor - "the new prototype"
function Custom(obj) {
    for ( prop in obj ) {
        if ( obj.hasOwnProperty(prop) ) {
            this[prop] = obj[prop];
        }
    }
}

// the properties of the new prototype
Custom.prototype.bar = true;

// pass your original object into the constructor
var obj2 = new Custom(obj);

// the constructor instance contains all properties from the original 
// object and also all properties inherited by the new prototype
obj2.foo; // true
obj2.bar; // true

现场演示: http : //jsfiddle.net/6Xq3P/

Custom构造代表新的原型,ERGO,其Custom.prototype对象包含所有你想和你的原始对象要使用的新属性。

Custom构造函数中,您只需将原始对象的所有属性复制到新的实例对象。

这个新的实例对象包含来自原始对象的所有属性(它们在构造函数中被复制到它),以及内部定义的所有新属性Custom.prototype(因为新对象是一个Custom实例)。