programing tip

이미 인스턴스화 된 JavaScript 객체의 프로토 타입을 설정하는 방법은 무엇입니까?

itbloger 2020. 8. 18. 07:25
반응형

이미 인스턴스화 된 JavaScript 객체의 프로토 타입을 설정하는 방법은 무엇입니까?


foo내 JavaScript 코드에 개체가 있다고 가정 합니다. foo복잡한 개체이며 다른 곳에서 생성됩니다. foo개체 의 프로토 타입을 어떻게 변경할 수 있습니까?

내 동기는 .NET에서 JavaScript 리터럴로 직렬화 된 객체에 적절한 프로토 타입을 설정하는 것입니다.

ASP.NET 페이지에서 다음 JavaScript 코드를 작성했다고 가정합니다.

var foo = <%=MyData %>;

객체 MyData에서 .NET JavaScriptSerializer호출 한 결과 라고 가정 Dictionary<string,string>합니다.

런타임시 다음과 같이됩니다.

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

보시다시피 foo는 객체의 배열이됩니다. foo적절한 프로토 타입 으로 초기화 하고 싶습니다 . 나는 할 수 없습니다 수정하려는 Object.prototypeArray.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]] 체인을 통해 올라갑니다. 이것이 자바 스크립트가 객체의 런타임 빌드 및 수정을 허용하는 방법입니다. 필요한 것을 검색 할 계획이 있습니다.

__proto__속성은 (지금은 많이) 일부 구현에 존재 : 모질라 구현, 내가 아는 모든 웹킷 사람, 어떤 다른 사람을. 이 속성은 내부 [[prototype]] 속성을 가리키며 객체 생성 후 수정을 허용합니다. 이 연결 조회로 인해 모든 속성 및 기능이 프로토 타입과 일치하도록 즉시 전환됩니다.

이 기능은 현재 표준화되었지만 여전히 JavaScript의 필수 부분은 아니며이를 지원하는 언어에서는 코드가 "최적화되지 않은"범주로 떨어질 가능성이 높습니다. JS 엔진은 코드, 특히 자주 액세스되는 "핫"코드를 분류하기 위해 최선을 다해야하며 수정과 같은 멋진 작업을 수행하는 경우 __proto__코드를 전혀 최적화하지 않습니다.

이 게시물은 https://bugzilla.mozilla.org/show_bug.cgi?id=607863의 현재 구현 __proto__과 그 차이점에 대해 구체적으로 설명 합니다. 모든 구현은 어렵고 해결되지 않은 문제이기 때문에 다르게 수행합니다. a.) 구문 b.) 호스트 객체 (DOM은 기술적으로 Javascript 외부에 있음) 및 c.)를 제외하고 Javascript의 모든 것은 변경 가능합니다 __proto__. 나머지는 전적으로 귀하와 다른 모든 개발자의 손에 있으므로 __proto__엄지 손가락처럼 튀어 나온 이유를 알 수 있습니다 .

다른 방법으로는 __proto__불가능한 일이 있습니다. 런타임시 생성자와 별개로 객체 프로토 타입을 지정하는 것입니다. 이것은 중요한 사용 사례이며 __proto__아직 죽지 않은 주된 이유 중 하나입니다 . Harmony 공식화에서 진지한 논의가되거나 곧 ECMAScript 6으로 알려질만큼 중요합니다. 생성 중에 객체의 프로토 타입을 지정하는 기능은 다음 버전의 Javascript에 포함될 것입니다. __proto__의 날을 나타내는 종 은 공식적으로 번호가 매겨져 있습니다.

단기적 __proto__으로는이를 지원하는 브라우저를 대상으로 하는 경우 사용할 수 있습니다 (IE가 아니고 IE도 지원하지 않음). ES6는 2013 년까지 완성되지 않기 때문에 향후 10 년 동안 웹킷과 moz에서 작동 할 것입니다.

Brendan Eich -re : ES5의 새로운 Object 메서드 접근 방식 :

죄송합니다. ...하지만 settable __proto__은 객체 이니셜 라이저 사용 사례 (예 : 아직 도달 할 수없는 새 객체에 대해 ES5의 Object.create와 유사 함)를 제외하고는 끔찍한 아이디어입니다. 나는 __proto__12 년 전에 settable을 설계하고 구현 한 이것을 씁니다 .

... 계층화의 부족이 문제입니다 (키가있는 JSON 데이터 고려 "__proto__"). 그리고 더 나쁜 것은 가변성이 있다는 것은 구현이 ilooping을 피하기 위해 순환 프로토 타입 체인을 확인해야 함을 의미합니다. [무한 재귀에 대한 지속적인 검사 필요]

마지막으로 __proto__기존 객체를 변경 하면 새 프로토 타입 객체에서 제네릭이 아닌 메서드가 손상 될 수 있으며, 이는 __proto__설정중인 수신자 (직접) 객체에서 작동 할 수 없습니다 . 이것은 일반적으로 의도적 인 유형 혼란의 한 형태 인 단순히 나쁜 습관입니다.


ES6는 마지막으로 Chrome과 Firefox에서 이미 구현 된 Object.setPrototypeOf (object, prototype)지정합니다 .


constructor개체의 인스턴스에서를 사용하여 개체의 프로토 타입을 내부에서 변경할 수 있습니다 . 나는 이것이 당신이 요구하는 것이라고 믿습니다.

이것은 다음 foo의 인스턴스가있는 경우를 의미합니다 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__ = 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__와 더 이상 사용되지 않는 이유에 대한 설명 도 참조 하십시오 .


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/

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

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

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 Custom instance).


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:

  1. changing the non standard/cross browser __proto__ property
  2. Copy the Objects properties to a new object

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:

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

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

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

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

  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

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.


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.

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?


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.


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

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?

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"}]

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

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

참고URL : https://stackoverflow.com/questions/7015693/how-to-set-the-prototype-of-a-javascript-object-that-has-already-been-instantiat

반응형