[javascript] 이미 인스턴스화 된 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__와 더 이상 사용되지 않는 이유에 대한 설명 도 참조 하십시오 .


답변

프록시 생성자 함수를 정의한 다음 새 인스턴스를 만들고 원본 개체의 모든 속성을 여기에 복사 할 수 있습니다.

// 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생성자는 새로운 프로토 타입, 에고, 그 나타내는 Custom.prototype개체는 원래 개체에 사용하려는 모든 새로운 속성이 포함되어 있습니다.

Custom생성자 내에서 원본 객체의 모든 속성을 새 인스턴스 객체로 복사하기 만하면됩니다.

이 새 인스턴스 개체에는 원본 개체의 모든 속성 (생성자 내부에 복사 됨)과 내부에 정의 된 모든 새 속성 Custom.prototype(새 개체가 Custom인스턴스 이기 때문에 )이 포함됩니다.


답변

크로스 브라우저 방식으로 이미 인스턴스화 된 JavaScript 객체의 프로토 타입은 변경할 수 없습니다. 다른 사람들이 언급했듯이 옵션은 다음과 같습니다.

  1. 비표준 / 교차 브라우저 __proto__속성 변경
  2. 개체 속성을 새 개체에 복사

특히 전체 프로토 타입 요소를 효과적으로 변경하기 위해 객체를 내부 객체로 재귀 적으로 반복해야하는 경우 특히 좋습니다.

질문에 대한 대체 솔루션

원하는 기능을 좀 더 추상적으로 살펴 보겠습니다.

기본적으로 프로토 타입 / 메소드는 객체를 기반으로 함수를 그룹화하는 방법을 허용합니다.
쓰는 대신

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

당신은 쓰기

'   test  '.trim();

위의 구문은 object.method () 구문 때문에 OOP라는 용어로 만들어졌습니다. 기존 함수형 프로그래밍에 비해 OOP의 주요 이점 중 일부는 다음과 같습니다.

  1. 짧은 메서드 이름과 더 적은 변수 obj.replace('needle','replaced')와 같은 이름과 str_replace ( 'foo' , 'bar' , 'subject')다른 변수의 위치 를 기억해야 함
  2. 메소드 체이닝 ( string.trim().split().join())은 중첩 함수를 수정하고 작성하기가 더 쉬울 수 있습니다.join(split(trim(string))

불행히도 JavaScript에서는 (위에 표시된대로) 이미 존재하는 프로토 타입을 수정할 수 없습니다. 이상적으로는 위 Object.prototype의 주어진 Object에 대해서만 수정할 수 있지만 불행히도 수정 Object.prototype하면 잠재적으로 스크립트가 손상 될 수 있습니다 (속성 충돌 및 재정의 결과).

이 두 가지 프로그래밍 스타일 사이에는 일반적으로 사용되는 중간 지점이 없으며 사용자 지정 함수를 구성하는 OOP 방식도 없습니다.

UnlimitJS 는 사용자 지정 메서드를 정의 할 수있는 중간 지점을 제공합니다. 다음을 방지합니다.

  1. 객체의 프로토 타입을 확장하지 않기 때문에 속성 충돌
  2. 여전히 OOP 체인 구문을 허용합니다.
  3. Unlimit의 JavaScript 프로토 타입 속성 충돌 문제가 많은 450 바이트 크로스 브라우저 (IE6 +, Firefox 3.0 +, Chrome, Opera, Safari 3.0+) 스크립트입니다.

위의 코드를 사용하여 개체에 대해 호출하려는 함수의 네임 스페이스를 간단히 만들 것입니다.

다음은 예입니다.

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]()함수 를 호출 할 때 해당 함수를 Object의 메서드로 호출 할 수 있습니다. OOP와 기능 도로 사이의 중간 지점과 같습니다.


답변

[[prototype]]내가 아는 한 이미 구성된 객체 의 참조를 변경할 수 없습니다 . 원래 생성자 함수의 프로토 타입 속성을 변경할 수 있지만 이미 주석을 달았 듯이 해당 생성자는 Object이고 핵심 JS 구성을 변경하는 것은 나쁜 일입니다.

하지만 필요한 추가 기능을 구현하는 생성 된 개체의 프록시 개체를 만들 수 있습니다. 또한 문제의 객체에 직접 할당하여 추가 메서드와 동작을 monkeypatch 할 수도 있습니다.

다른 각도에서 기꺼이 접근하려는 경우 다른 방법으로 원하는 것을 얻을 수 있습니다. 프로토 타입을 엉망으로 만드는 것과 관련하여 무엇을해야합니까?