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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7015693/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:16:34  来源:igfitidea点击:

How to set the prototype of a JavaScript object that has already been instantiated?

javascriptfunction-prototypes

提问by Vivian River

Suppose I have an object fooin my JavaScript code. foois a complex object and it is generated somewhere else. How can I change the prototype of the fooobject?

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

My motivation is setting appropriate prototypes to objects serialized from .NET to JavaScript literals.

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

Suppose that I've written the following JavaScript code within an ASP.NET page.

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

var foo = <%=MyData %>;

Suppose that MyDatais the result of invoking the .NET JavaScriptSerializeron a Dictionary<string,string>object.

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

At run-time, this becomes the following:

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

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

As you can see, foobecomes an array of objects. I would like to be able to initialize foowith an appropriate prototype. I do notwant to modify the Object.prototypenor Array.prototype. How can I do this?

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

回答by

EDIT Feb. 2012: the answer below is no longer accurate. __proto__ is being added to ECMAScript 6 as "normative optional" which means it isn't required to be implemented but if it is, it must follow the given set of rules. This is currently unresolved but at least it will be officially part of JavaScript's specification.

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

This question is a lot more complicated than it seems on the surface, and beyond most peoples' pay grade in regards to knowledge of Javascript internals.

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

The prototypeproperty of an object is used when creating new child objects of that object. Changing it does not reflect in the object itself, rather is reflected when that object is used as a constructor for other objects, and has no use in changing the prototype of an existing object.

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

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

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

Objects have an internal [[prototype]] property which points to the current prototype. The way it works is whenever a property on an object is called it will start at the object and then go up through the [[prototype]] chain until it finds a match, or fail, after the root Object prototype. This is how Javascript allows for runtime building and modification of objects; it has a plan for searching for what it needs.

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

The __proto__property exists in some implementations (a lot now): any Mozilla implementation, all the webkit ones I know of, some others. This property points to the internal [[prototype]] property and allows modification post-creation on objects. Any properties and functions will instantly switch to match the prototype due to this chained lookup.

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

This feature, while being standardized now, still is not a required part of JavaScript, and in languages supporting it has a high likelihood of knocking your code down into the "unoptimized" category. JS engines have to do their best to classify code, especially "hot" code which is accessed very often, and if you're doing something fancy like modifying __proto__, they won't optimize your code at all.

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

This posts https://bugzilla.mozilla.org/show_bug.cgi?id=607863specifically discusses current implementations of __proto__and the differences between them. Every implementation does it differently, because it's a hard and unsolved problem. Everything in Javascript is mutable, except a.) the syntax b.) host objects (the DOM exists outside Javascript technically) and c.) __proto__. The rest is completely in the hands of you and every other developer, so you can see why __proto__sticks out like a sore thumb.

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

There is one thing that __proto__allows for that is otherwise impossible to do: the designation of an objects prototype at runtime separate from its constructor. This is an important use case and is one of the primary reasons __proto__isn't already dead. It's important enough that it's been a serious discussion point in the formulation of Harmony, or soon to be known as ECMAScript 6. The ability to specify the prototype of an object during creation will be a part of the next version of Javascript and this will be the bell indicating __proto__'s days are formally numbered.

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

In the short term, you can use __proto__if you're targeting browsers that support it (not IE, and no IE ever will). It's likely it'll work in webkit and moz for the next 10 years as ES6 won't be finalized until 2013.

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

Brendan Eich- re:Approach of new Object methods in ES5:

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

Sorry, ... but settable __proto__, apart from the object initialiser use case (i.e., on a new object not yet reachable, analogous to ES5's Object.create), is a terrible idea. I write this having designed and implemented settable __proto__over 12 years ago.

... the lack of stratification is a problem (consider JSON data with a key "__proto__"). And worse, the mutability means implementations must check for cyclic prototype chains in order to avoid ilooping. [constant checks for infinite recursion are required]

Finally, mutating __proto__on an existing object may break non-generic methods in the new prototype object, which cannot possibly work on the receiver (direct) object whose __proto__is being set. This is simply bad practice, a form of intentional type confusion, in general.

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

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

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

回答by Joel Richard

ES6 finally specifies Object.setPrototypeOf(object, prototype)which is already implemented in Chrome and Firefox.

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

回答by Tim

You can use constructoron an instance of an object to alter the prototype of an object in-place. I believe this is what you're asking to do.

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

This means if you have foowhich is an instance of Foo:

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

function Foo() {}

var foo = new Foo();

You can add a property barto all instances of Fooby doing the following:

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

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

Here's a fiddle showing the proof-of-concept: http://jsfiddle.net/C2cpw/. Not terribly sure how older browsers will fare using this approach, but I'm pretty sure this should do the job pretty well.

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

If your intention is to mixin functionality into objects, this snippet should do the job:

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

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;
    }
  };
};

回答by Wladimir Palant

You can do foo.__proto__ = FooClass.prototype, AFAIK that's supported by Firefox, Chrome and Safari. Keep in mind that the __proto__property is non-standard and might go away at some point.

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

Documentation: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto. Also see http://www.mail-archive.com/[email protected]/msg00392.htmlfor an explanation why there is no Object.setPrototypeOf()and why __proto__is deprecated.

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

回答by ?ime Vidas

You could define your proxy constructor function and then create a new instance and copy all the properties from the original object to it.

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

// 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

Live demo:http://jsfiddle.net/6Xq3P/

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

The Customconstructor represents the new prototype, ergo, its Custom.prototypeobject contains all the new properties which you'd like to use with your original object.

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

Inside the Customconstructor, you just copy all the properties from the original object to the new instance object.

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

This new instance object contains all the properties from the original object (they were copied to it inside the constructor), and also all the new properties defined inside Custom.prototype(because the new object is a Custominstance).

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

回答by Lime

You can't change the prototype of a JavaScript object that has already been instantiated in a cross browser way. As others have mentioned, your options include:

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

  1. changing the non standard/cross browser __proto__property
  2. Copy the Objects properties to a new object
  1. 更改非标准/跨浏览器__proto__属性
  2. 将对象属性复制到新对象

Neither are particularly great, especially if you have to recursively loop through an object into inner objects to effectively change an elements entire prototype.

两者都不是特别好,特别是如果您必须递归地将对象循环到内部对象中以有效地更改元素的整个原型。

Alternative solution to question

问题的替代解决方案

I'm going to take a more abstract look at the functionality it appears you desire.

我将更抽象地看待您想要的功能。

Basically prototype/methods just allow for a way to group functions based on an object.
Instead of writing

基本上原型/方法只允许一种基于对象对函数进行分组的方法。
而不是写作

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

you write

你写

'   test  '.trim();

The above syntax has been coined the term OOP because of the object.method() syntax. Some of OOPs main advantage over traditional functional programming includes:

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

  1. Short methods names and fewer variables obj.replace('needle','replaced')vs having to remember names like str_replace ( 'foo' , 'bar' , 'subject')and the location of the different variables
  2. method chaining(string.trim().split().join()) is a potentially easier to modify and write then nested functions join(split(trim(string))
  1. 简短的方法名称和更少的变量obj.replace('needle','replaced')vs 必须记住str_replace ( 'foo' , 'bar' , 'subject')不同变量的名称和位置
  2. 方法 chaining( string.trim().split().join()) 可能更容易修改和编写嵌套函数join(split(trim(string))

Unfortunately in JavaScript (as shown above) you can't modify an already existent prototype. Ideally above you could modify Object.prototypefor only the given Object's above, but unfortunately modifying Object.prototypewould potentially break scripts (resulting in property collision and overriding).

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

There is no commonly used middle ground between these 2 styles of programming, and no OOP way to organize custom functions.

这两种编程风格之间没有常用的中间立场,也没有组织自定义函数的 OOP 方式。

UnlimitJSprovides a middle ground that allows you to define custom methods. It avoids:

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

  1. Property collision, because it doesn't extend Objects' prototypes
  2. Still allows for an OOP chaining syntax
  3. It is 450 byte cross browser(IE6+,Firefox 3.0+,Chrome,Opera,Safari 3.0+) script that Unlimit's much of JavaScript's prototype property collision issues
  1. 属性碰撞,因为它不扩展对象的原型
  2. 仍然允许 OOP 链接语法
  3. Unlimit 的大部分 JavaScript 原型属性冲突问题是 450 字节跨浏览器(IE6+、Firefox 3.0+、Chrome、Opera、Safari 3.0+)脚本

Using your code above I would simply create a namespace of functions that you intend to call against the object.

使用上面的代码,我只需创建一个您打算针对对象调用的函数的命名空间。

Here is an example:

下面是一个例子:

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]();

You can read more of the examples here UnlimitJS. Basically when you call [Unlimit]()on a function, it allows for the function to be called as a method on an Object. It's like a middle ground between the OOP and functional roads.

您可以在此处阅读更多示例UnlimitJS。基本上,当您调用[Unlimit]()一个函数时,它允许将该函数作为对象上的方法来调用。它就像是 OOP 和功能性道路之间的中间地带。

回答by Nick Husher

You cannot change the [[prototype]]reference of already-constructed objects, as far as I know. You could alter the the prototype property of the original constructor function but, as you've already commented, that constructor is Object, and altering core JS constructs is a Bad Thing.

[[prototype]]据我所知,您不能更改已构建对象的引用。您可以更改原始构造函数的原型属性,但是,正如您已经评论过的,该构造函数是Object,而更改核心 JS 构造是一件坏事。

You could create a proxy object of the constructed object that implements the additional functionality that you need, though. You could also monkeypatch the additional methods and behaviors by assigning directly to the object in question.

不过,您可以创建构造对象的代理对象,以实现您需要的附加功能。您还可以通过直接分配给相关对象来对其他方法和行为进行猴子补丁。

Perhaps you can get what you want some other way, if you're willing to approach from a different angle: What do you need to do that involves messing with the prototype?

也许你可以通过其他方式得到你想要的东西,如果你愿意从不同的角度接近:你需要做什么涉及弄乱原型?

回答by rewritten

If you know the prototype, why not injecting it in the code?

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

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

So, once the data is serialized, you get

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

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

now you only need a constructor that takes an array as argument.

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

回答by Yene Mulatu

if you want to create prototype on the fly, this is one of the way

如果您想即时创建原型,这是其中一种方法

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);

回答by Ricardo Tomasi

There is no way to really inherit from Arrayor "sub-class" it.

没有办法真正继承Array或“子类”它。

What you can do is this (WARNING: 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

This works, but will cause certain trouble for anyone that crosses it's path (it looks like an array, but things will go wrong if you try manipulating it).

这行得通,但会给任何跨越它的路径的人带来一定的麻烦(它看起来像一个数组,但如果你尝试操作它,事情就会出错)。

Why don't you go the sane route and add methods/properties directly to foo, or use a constructor and save your array as a property?

为什么不走理智的路线,直接将方法/属性添加到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"}]