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

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

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

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

 var foo = <%=MyData %>; 

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

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

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

正如你所看到的, foo变成了一个对象数组。 我希望能够用适当的原型来初始化foo 。 我不想修改Object.prototypeArray.prototype 。 我怎样才能做到这一点?

编辑2012年2月:下面的答案不再准确。 __proto__被添加到ECMAScript 6中作为“规范可选”,这意味着它不需要实现,但是如果是,它必须遵循给定的规则集。 目前这个问题还没有解决,但至less它是JavaScript规范的正式组成部分。

这个问题比表面上看起来要复杂得多,在JavaScript内部知识方面超出了大多数人的薪酬等级。

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

 function myFactory(){}; myFactory.prototype = someOtherObject; var newChild = new myFactory; newChild.__proto__ === myFactory.prototype === someOtherObject; //true 

对象具有指向当前原型的内部[[prototype]]属性。 它的工作方式是每当一个对象的属性被调用时,它将从该对象开始,然后遍历[[prototype]]链直到find匹配,或者在根对象原型之后失败。 这是Javascript如何允许运行时build立和修改对象; 它有一个计划去寻找它需要的东西。

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

这个function虽然现在已经标准化了,但仍然不是JavaScript的必要组成部分,在支持它的语言中,很有可能把你的代码敲入“未优化”的类别。 JS引擎必须尽最大努力对代码进行分类,特别是经常访问的“热门”代码,如果你想做一些像修改__proto__这样的东西,他们根本不会优化你的代码。

这篇文章https://bugzilla.mozilla.org/show_bug.cgi?id=607863特别讨论;__proto__当前实现及其差异。 每个实现都不一样,因为这是一个难以解决的问题。 JavaScript中的所有东西都是可变的,除了a。)语法b。)宿主对象(DOM在技术上存在于Javascript之外)和c。) __proto__ 。 其余的完全掌握在你和其他开发者手中,所以你可以看到为什么__proto__像拇指一样伸出手。

有一件事__proto__允许这是不可能的:在运行时指定一个对象的原型与其构造函数分开。 这是一个重要的用例,也是__proto__还没有死的主要原因之一。 重要的是它已经成为和谐的一个严肃的讨论点,或者很快被称为ECMAScript 6.在创build过程中指定对象的原型的能力将成为下一个版本的Javascript的一部分,这将是指示__proto__日子的钟声正式编号。

在短期内,如果你的浏览器支持它(不是IE,IE也不会),你可以使用__proto__ 。 未来十年,它可能会在webkit和moz中工作,因为ES6将在2013年才能最终完成。

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

对不起,…但可设置的__proto__ ,除了对象初始化用例(即,对于一个新对象不可达,类似于ES5的Object.create),是一个可怕的想法。 我写这个已经在12年前devise和实现了可设置的__proto__

…缺乏分层是一个问题(考虑使用键"__proto__" JSON数据)。 更糟的是,可变性意味着实现必须检查循环原型链,以避免il </s>。 [不断检查无限recursion是必需的]

最后,在现有对象上对__proto__进行变异可能会破坏新原型对象中的非generics方法,这对于设置了__proto__的receiver(direct)对象来说是不可能的。 一般来说,这只是不好的做法,一种故意types混淆的forms。

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

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

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

 function Foo() {} var foo = new Foo(); 

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

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

这是一个小提琴展示的概念validation: http : //jsfiddle.net/C2cpw/ 。 不是很确定使用这种方法的旧版浏览器会如何,但是我非常肯定这应该能够很好地完成这项工作。

如果你的目的是把function混入对象中,这个片段应该做这个工作:

 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__ = FooClass.prototype ,AFAIK,它支持Firefox,Chrome和Safari。 请记住, __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__不推荐使用。

您无法更改已经以跨浏览器方式实例化的JavaScript对象的原型。 正如其他人所说,你的select包括:

  1. 更改非标准/跨浏览器__proto__属性
  2. 将对象属性复制到一个新的对象

两者都不是特别好,特别是如果你必须recursion地循环一个对象到内部对象中来有效地改变一个元素的整个原型。

问题的替代解决scheme

我将更抽象地看看你想要的function。

基本上,原型/方法只是允许基于对象对function进行分组的方式。
而不是写作

 function trim(x){ /* implementation */ } trim(' test '); 

你写

 ' test '.trim(); 

由于object.method()语法,上面的语法已经被称为术语OOP。 OOP的一些主要优势比传统的函数式编程包括:

  1. 简短的方法名称和更less的variablesobj.replace('needle','replaced') vs不得不记住名字str_replace ( 'foo' , 'bar' , 'subject')和不同variables的位置
  2. 方法chaining( string.trim().split().join() )是一个可能更容易修改和编写嵌套函数join(split(trim(string))

不幸的是,在JavaScript中(如上所示),你不能修改已经存在的原型。 理想情况下,你可以修改Object.prototype只有上面给定的对象,但不幸的是修改Object.prototype可能会破坏脚本(导致属性冲突和重写)。

在这两种编程风格之间没有常用的中间地带,也没有用于组织自定义函数的OOP方法。

UnlimitJS提供了一个中间的基础,允许您定义自定义方法。 它避免:

  1. 属性冲突,因为它不扩展对象的原型
  2. 仍然允许使用OOP链接语法
  3. 它是450字节的跨浏览器(IE6 +,Firefox 3.0 +,Chrome,Opera,Safari 3.0+)脚本,Unlimit的大部分JavaScript的原型属性碰撞问题

使用上面的代码我只是简单地创build一个函数的名称空间,你打算调用对象。

这里是一个例子:

 var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}]; // define namespace with methods var $ = { log:function(){ console.log(this); return this; }[Unlimit](), alert:function(){ alert(''+this); }[Unlimit]() } foo[$.log]() [$.log]() [$.alert](); 

你可以在这里阅读更多的例子UnlimitJS 。 基本上,当你在一个函数上调用[Unlimit]()时,它允许函数作为一个对象的方法被调用。 这就像面向对象和function性道路之间的中间地带。

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

 // 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实例)。

就我所知,你不能改变已经构build的对象的[[prototype]]引用。 你可以改变原始构造函数的原型属性,但是,正如你已经评论过的那样,构造函数是Object ,改变核心JS结构是一件坏事。

不过,您可以创build构build对象的代理对象,以实现所需的附加function。 您也可以通过直接分配给所讨论的对象来修改附加的方法和行为。

也许你可以用其他方式得到你想要的东西,如果你愿意从一个不同的angular度来看:你需要做什么涉及到原型的混乱?

如果你知道原型,为什么不把它注入代码?

 var foo = new MyPrototype(<%= MyData %>); 

所以,一旦数据被序列化,你会得到

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

现在只需要一个以数组为参数的构造函数。

 foo.prototype.myFunction = function(){alert("me");} 

没有办法真正从Array或“sub-class”中inheritance它。

你可以做的是这个( 警告:FESTERING CODE AHEAD ):

 function Foo(arr){ [].push.apply(this, arr) } Foo.prototype = [] Foo.prototype.something = 123 var foo = new Foo(<%=MyData %>) foo.length // => 2 foo[0] // => {"A":"1","B":"2"} foo.something // => 123 

这样做会起作用,但是对于任何跨越path的人来说都会造成一定的麻烦(看起来像一个数组,但是如果你尝试操作,会出错)。

你为什么不走理性的路线,直接添加方法/属性到foo ,或者使用一个构造函数并将你的数组保存为一个属性?

 function Foo(arr){ this.items = arr } Foo.prototype = { someMethod : function(){ ... } //... } var foo = new Foo(<%=MyData %>) foo.items // => [{"A":"1","B":"2"},{"X":"7","Y":"8"}] 

如果你想在飞行中创build原型,这是其中一个方法

 function OntheFlyProto (info){ this.items = info; this.y =-1; for(var i = 0; i < this.items.length ; i++){ OntheFlyProto.prototype["get"+this.items[i].name] = function (){ this.y++; return this.items[this.y].value; } } } var foo = [{name:"one", value:1},{name:"two", value:2}]; v = new OntheFlyProto(foo);