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

Dan*_*don 101 javascript function-prototypes

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

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

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

var foo = <%=MyData %>;
Run Code Online (Sandbox Code Playgroud)

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

在运行时,这将变为以下内容:

var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Run Code Online (Sandbox Code Playgroud)

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

小智 111

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

这个问题比表面看起来要复杂得多,并且超出了大多数人对Javascript内部知识的薪酬等级.

prototype创建该对象的新的子对象时,对象的属性使用.更改它不会反映在对象本身中,而是在该对象用作其他对象的构造函数时反映,并且无法用于更改现有对象的原型.

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

var newChild = new myFactory;
newChild.__proto__ === myFactory.prototype === someOtherObject; //true
Run Code Online (Sandbox Code Playgroud)

对象具有指向当前原型的内部[[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中新对象方法的方法:

抱歉,...但是可设置__proto__,除了对象初始化用例(即,在一个尚未到达的新对象上,类似于ES5的Object.create),这是一个可怕的想法.我__proto__在12年前设计并实现了可设置的这个.

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

最后,__proto__对现有对象进行变异可能会破坏新原型对象中的非泛型方法,这些方法不可能对__proto__正在设置的接收方(直接)对象起作用.这通常是一种不好的做法,一种故意类型混淆的形式.

  • Brendan Eich的问题是整个原型语言的原生.可能使\ _ _ _ proto\_\_ _`不可写,可配置`将通过强制用户显式重新配置属性来解决这些问题.最后,不良做法与滥用语言能力有关,而与能力本身无关.可写\ _\_\_原始\ _\_ _是不是闻所未闻的.还有许多其他不可枚举的可写属性,虽然存在危险,但也有最佳实践.锤子的头部不应该被简单地拆除,因为它可以不正确地用于伤害某人. (2认同)
  • 如果没有使用魔法属性但是使用Object.setPrototype,消除"JSON中的`__proto__`"关注会更好.Brendan Eich的其他担忧是可笑的.递归原型链或使用给定对象的方法不充分的原型是程序员错误,而不是语言错误,不应该是一个因素,而且它们可以与Object.create一起发生,也可以与可自由设置的`__proto__`一起发生. (2认同)

Joe*_*ard 31

ES6最终指定了已经在Chrome和Firefox中实现的Object.setPrototypeOf(对象,原型).


Tim*_*Tim 14

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

这意味着如果你有foo一个实例Foo:

function Foo() {}

var foo = new Foo();
Run Code Online (Sandbox Code Playgroud)

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

foo.constructor.prototype.bar = "bar";
Run Code Online (Sandbox Code Playgroud)

这是一个显示概念验证的小提琴: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;
    }
  };
};
Run Code Online (Sandbox Code Playgroud)


Wla*_*ant 9

你可以做到foo.__proto__ = FooClass.prototype,Firefox,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__弃用的原因.