[JavaScript] getter/setter

참고 문서

브라우저 호환

  • IE8 이하는 사용 불가

개요

ECMAScript 5.1에서 최초로 정의된 구문. 프로퍼티처럼 접근할 수 있는 함수를 정의한다.

{ get prop() { ... } }
{ get [expression]() { ... } }

{ set prop(val) { . . . } }
{ set [expression](val) { . . . } }

getter와 setter는 접근자 프로퍼티(accessor property)에 속하는데, 우리가 일반적으로 사용하는 데이터 프로퍼티(data property)와는 다르게 취급된다. 예를 들면, getter/setter는 Object.getOwnPropertyDescriptor()에서 보이지 않는다.

class 선언에서

class Newbie {
  get trait() {
    return this._trait || 'know nothing';
  }
  set trait(arg) {
    this._trait = arg;
  }
}

var noob = new Newbie();

noob.trait; // "know nothing"
noob.trait = 'crawl';
noob.trait; // "crawl"

객체 리터럴에서

var obj = {
  _findMe: 'Hello',
  get findMe() {
    // return this._findMe;
    return 'Nope';
  },
  set findMe(value) {
    // this._findMe = value;
    console.log('Denied');
  }     
};

obj._findMe; // "Hello"
obj.findMe; // "Nope"
obj.findMe = 1234; // Denied
obj.findMe; // "Nope"

재정의 불가능한 프로퍼티

요딴식으로 setter 없이 getter만 정의하면:

class Newbie {
  get name() {
    return 'fresh newbie';
  }
}

var noob = new Newbie();
noob.name; // "fresh newbie";
noob.name = 'spoiled'; // strict 모드일 땐 TypeError: setting getter-only property 'name' 발생함.
noob.name; // "fresh newbie";

name은 일종의 immutable property가 된다.

객체 복제에 포함되지 않음

getter/setter는 enumerable한 프로퍼티가 아니므로, Object.assign()이나 전개 구문으로 복제할 때 제외된다:

var noob = new Newbie();
noob.trait = 'eating';
noob; // Object { _trait: "eating" }

var clone = {...noob};
clone.trait = 'sleeping';
clone; // Object { _trait: "eating", trait: "sleeping" }

var clone2 = Object.assign({}, noob);
clone2.trait = 'do nothing';
clone2 // Object { _trait: "eating", trait: "do nothing" }

프로퍼티 감시에 사용하기

setter는 프로퍼티의 값을 바꿀 때마다 실행되므로 프로퍼티의 변경을 감시하는 watcher로 사용할 수 있다:

var obj = {
  get foo() {
    console.log({name: 'foo', object: obj, type: 'get'});
    return obj._foo;
  },
  set bar(val) {
    console.log({name: 'bar', object: obj, type: 'set', oldValue: obj._bar});
    return obj._bar = val;
  }
};

obj.bar = 2;
// {name: 'bar', object: <obj>, type: 'set', oldValue: undefined}

obj.foo;
// {name: 'foo', object: <obj>, type: 'get'}