프로토 타입에서 setter / getter를 정의하는 방법


78

2016 년 10 월 수정 :이 질문은 2012 년에 요청되었습니다. 매달 누군가가 답변을 반박하는 새로운 답변이나 댓글을 추가하지만 질문이 오래되었을 수 있으므로 그렇게하는 것이 의미가 없습니다. Gnome Javascript 가 브라우저 항목이 아닌 그놈 쉘 확장을 작성 하는 것이 었습니다 .

Javascript에서 하위 클래스를 수행하는 방법에 대한 이전 질문 에 따라 다음 과 같이 수퍼 클래스의 하위 클래스를 만들고 있습니다.

function inherits(Child,Parent) {
    var Tmp = function {};
    Tmp.prototype = Parent.prototype;
    Child.prototype = new Tmp();
    Child.prototype.constructor = Child;
}
/* Define subclass */
function Subclass() {
    Superclass.apply(this,arguments);
    /* other initialisation */
}
/* Set up inheritance */
inherits(Subclass,Superclass);
/* Add other methods */
Subclass.prototype.method1 = function ... // and so on.

내 질문은 이 구문을 사용하여 프로토 타입에서 setter / getter를 어떻게 정의합니까?

나는 그것을 하고는했다:

Subclass.prototype = {
    __proto__: Superclass.prototype,
    /* other methods here ... */

    get myProperty() {
        // code.
    }
}

그러나 분명히 다음은 작동하지 않습니다.

Subclass.prototype.get myProperty() { /* code */ }

저는 GJS (GNOME Javascript)를 사용하고 있으며 엔진은 Mozilla Spidermonkey와 거의 동일합니다. 내 코드는 GJS에서 지원하는 한 브라우저 용이 아닙니다 (Spidermonkey를 의미합니까?). 교차 호환이되지 않아도 상관 없습니다.


Mozilla 문서는 언급 __defineGetter__하고 __defineSetter(하지만 실제로 사용하지 않았습니다 ...). developer.mozilla.org/en/Core_JavaScript_1.5_Guide/…
bfavaretto

환상적이야, 내가 쫓는 것 같아. 답변으로 게시하면 수락하겠습니다. 건배! :)
mathematical.coffee

그것을 완료하고 MDN의 예제를 추가했습니다.
bfavaretto

1
이러지 마세요. JS의 상속은 세 줄로 구성됩니다. 수퍼 클래스를 호출하고 프로토 타입을 수퍼 클래스로 설정하고 생성자를 다시 자식 클래스로 재설정합니다. 끝. 이와 같은 쓰기 방법은 완전한 시간 낭비입니다.
Hal50000

답변:


74

객체 리터럴 선언 사용 (가장 간단한 방법) :

var o = {
    a: 7,
    get b() {
        return this.a + 1;
    },
    set c(x) {
        this.a = x / 2
    }
};

사용 Object.defineProperty(ES5를 지원하는 최신 브라우저에서) :

Object.defineProperty(o, "myProperty", {
    get: function myProperty() {
        // code
    }
});

또는 __defineGetter__및 사용 __defineSetter__( DEPRECATED ) :

var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });

미안 해요, 제 실수입니다. es6에 도입 된 새로운 함수 표기법에 대해 잘못 읽었습니다.let foo = { bar () { return baz }}
royhowie

@royhowie 나는 ES5, 참조 get/ set구문은 새로운 방법 구문 : 영감을 것 같습니다
bfavaretto

getter에 대한 함수 이름을 다시 쓸 필요가 없다고 생각합니다. MDN 문서 예제는 익명 함수를 사용합니다.
StanE

또한 getter / setter의 람다에주의하십시오. this람다에서 전역 개체를 나타냅니다.
grabantot

102

사용 Object.defineProperty()Subclass.prototype. 이 또한 __defineGetter____defineSetter__일부 브라우저에서 사용할 수 있지만,이되지 않습니다. 귀하의 예를 들면 다음과 같습니다.

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});

1
"그들은 더 이상 사용되지 않습니다 (처럼 __proto__)"- __proto__ES6 초안에서 표준화되고 있음을 유의하십시오 .
Fabrício Matté 2013

@ FabrícioMatté : 알아요 (하지만 마음에 들지 않습니다). 그러나 여기서 요점은 Object.create대신 사용하는 것입니다.
Bergi

예, ES6가 표준이 된 후에는 사용되지 않는 것으로 간주되지 않는다는 메모를 추가했습니다 (IE11의 누출도 이미 구현 된 것으로 보입니다). 물론, 실제 환경에서 사용할 수있는 몇 가지 사용 사례에 대해 생각할 수있을 때까지는 오랜 시간이 걸립니다.
Fabrício Matté 2013

1
야! "프로토 타입에서 setter / getter [property]를 어떻게 정의합니까?" "당신은 그것을 프로토 타입의 속성으로 정의합니다."
Johann

2
@bob 귀하의 의견이 무엇을 의미하는지 잘 모르겠습니다. 물론, 기간을 동적으로 가져 오려면 getter가 필요하지만 다른 모든 메서드와 마찬가지로 다른 속성에서 작동 할 수 있습니다.Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }});
Bergi

41

나는 당신이 이렇게하고 싶었다고 생각합니다.

function Unit() {
   	this._data; // just temp value
}
Unit.prototype = {
 	get accreation() {
   		return this._data;
   	},
   	set accreation(value) {
   		this._data = value
   	},
}
Unit.prototype.edit = function(data) {
   	this.accreation = data; // setting
   	this.out();
};

Unit.prototype.out = function() {
    alert(this.accreation); // getting
};

var unit = new Unit();
unit.edit('setting and getting');

function Field() {
    // children
}

Field.prototype = Object.create(Unit.prototype);

Field.prototype.add = function(data) {
  this.accreation = data; // setting
   	this.out();
}

var field1 = new Field();
field1.add('new value for getter&setter');

var field2 = new Field();
field2.out();// because field2 object has no setting


2
놀랄 만한. 유일한 정답은 표를 얻지 못합니다. Object.defineProperty이전 IE의 모욕을 겪지 않는 한 사용 이유가 없습니다.
Hal50000

1
@ Hal50000 질문이 제기 된 지 거의 4 년 후에이 답변이 게시되었다는 사실을 무시하고 있습니다.
희미한 신호

1
@faintsignal 사실, 나 자신을 수정하기 위해 Object.defineProperty(), 열거 가능, 구성 가능, 쓰기 가능 등을 설정하고 싶을 때 위의 예 대신 사용 하는 몇 가지 이유가 있습니다 .하지만 당신이 맞습니다, 포스트 날짜를 살펴 보는 것이 좋습니다. 생각.
Hal50000

6
Unit.prototype.constructor이렇게 덮어 쓰면 지워집니다 Unit.prototype. 나는이 솔루션을 많이 좋아합니다. 아마도 당신은 할 수 있습니다 Object.assign(Unit.prototype, { get accreation() { ... } });.
jonS90

1
@ jonS90이 언급했듯이이 방법이 나쁜 생각 일 수있는 몇 가지 매우 유효한 이유가 있습니다. 클래스 상속 등을 잃게됩니다. 클래스 계층 구조를 사용하는 경우 대체 메서드를 사용하는 것이 좋습니다. 예를 들어이 메서드 를 사용하여 속성을 넣을 수 없습니다Field .
cmroanirgo

5

setter와 getter를 "객체의 프로토 타입 내부에"정의하려면 다음과 같이해야합니다.

Object.defineProperties(obj.__proto__, {"property_name": {get: getfn, set: setfn}})

유틸리티 함수로 단축 할 수 있습니다.

//creates get/set properties inside an object's proto
function prop (propname, getfn, setfn) {
    var obj = {};
    obj[propname] = { get: getfn, set: setfn };
    Object.defineProperties(this, obj);        
}

function Product () {
     this.name =  "Product";
     this.amount =  10;
     this.price =  1;
     this.discount =  0;
}

//how to use prop function
prop.apply(Product.prototype, ["total", function(){ return this.amount * this.price}]);

pr = new Product();
console.log(pr.total);

여기서는 prop.apply를 사용하여 Product.prototype을 호출 할 때 컨텍스트를 "this"로 설정합니다.

이 코드를 사용하면 질문에서 물었 듯이 인스턴스가 아닌 객체의 프로토 타입 내부에서 get / set 속성으로 끝납니다.

(테스트 된 Firefox 42, Chrome 45)


아주 좋아. 그러나 예제에서 setter를 적용하지 않았으므로 설정하려는 시도가 pr.total조용히 실패합니다. 실제로 getter 만 만들고 싶을 때 오류가 발생하도록 setter 함수를 전달하는 것이 가장 좋습니다.
희미한 신호

4

Object.defineProperty () 메서드를 사용하여 생성자에서 getter 또는 setter를 지정합니다. 이 메서드는 세 가지 인수를 사용합니다. 첫 번째 인수는 속성을 추가 할 개체이고 두 번째 인수는 속성 이름이며 세 번째 인수는 속성 설명자입니다. 예를 들어 다음과 같이 person 객체의 생성자를 정의 할 수 있습니다.

var Employee = (function() {
    function EmployeeConstructor() {
        this.first = "";
        this.last = "";
        Object.defineProperty(
            this,
            "fullName", {
                get: function() {
                    return this.first + " " +
                        this.last;
                },
                set: function(value) {
                    var parts = value.toString().split(" ");
                    this.name = parts[0] || "";
                    this.last = parts[1] || "";
                }
            });
    }
    return
    EmployeeConstructor;
}());

Object.defineProperty ()를 사용하면 속성 정의를 더 잘 제어 할 수 있습니다. 예를 들어, 설명하는 속성을 동적으로 삭제하거나 재정의 할 수 있는지, 값을 변경할 수 있는지 등을 지정할 수 있습니다.

설명자 객체의 다음 속성을 설정하여 이러한 제약을 적용 할 수 있습니다.

  • 쓰기 가능 : 속성 값을 변경할 수 있는지 여부를 나타내는 부울입니다. 기본값은 false입니다.
  • configurable : 속성의 설명자를 변경할 수 있는지 또는 속성 자체를 삭제할 수 있는지 여부를 나타내는 부울입니다. 기본값은 false입니다.
  • enumerable : 객체의 속성에 대한 루프에서 속성에 액세스 할 수 있는지 여부를 나타내는 부울입니다. 기본값은 false입니다.
  • value : 속성과 관련된 값을 나타냅니다. 기본값은 정의되지 않음

0

다음 은 gettersetter 가있는 간단한 Animal → Dog상속의 예입니다 .Animal

//////////////////////////////////////////
// General Animal constructor
function Animal({age, name}) {
  // if-statements prevent triggering the setter on initialization
  if(name) this.name = name
  if(age) this.age = age
}

// an alias "age" must be used, so the setter & getter can use an
// alternative variable, to avoid using "this.age", which will cause
// a stack overflow of "infinite" call stack when setting the value.
Object.defineProperty(Animal.prototype, "age", {
  get(){
    console.log("Get age:", this.name, this._age) // getting
    return this._age
  },
  set(value){
    this._age = value
    console.log("Set age:", this.name, this._age) // setting
  }
})




//////////////////////////////////////////
// Specific Animal (Dog) constructor
function Dog({age = 0, name = 'dog'}) {
  this.name = name
  this.age = age
}

// first, defined inheritance
Dog.prototype = new Animal({});

// add whatever additional methods to the prototype of Dog
Object.assign(Dog.prototype, {
  bark(woff){
    console.log(woff)
  }
})


//////////////////////////////////////////
// Instanciating
var koko = new Animal({age:300, name:'koko'})
var dog1 = new Dog({age:1, name:'blacky'})
var dog2 = new Dog({age:5, name:'shorty'})

console.log(dog1)
koko.age
dog1.age = 3;
dog1.age
dog2.age

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.