티스토리 뷰

JavaScript

[자바스크립트] getter, setter

버미노트 2017.01.13 10:08


JS


getter



getter는 객체의 프로퍼티를 가져오는 함수를 말합니다.



1. 문법

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


파라미터

prop

프로퍼티를 가져올 함수 이름


expression

ES6에서 추가되었습니다. 계산 되어지는 프로퍼티 이름을 위해 expression이 추가되었습니다. 밑에서 코드로 설명드리겠습니다.



2. 설명

동적으로 계산이 필요한 프로퍼티 값을 가져와야 할 때, getter를 사용한다면 별도의 함수를 만들 필요가 없습니다.


getter를 사용할 때 3가지에 유의해야 합니다.

1. 식별자로 숫자와 문자를 모두 사용할 수 있습니다.

2. 파라미터가 없어야 합니다.

3. 리터럴 객체의 같은 이름의 get이나 동일한 이름의 프로퍼티를 가질 수 없습니다. ({ get x() {}, get x() {} }, { x : ..., get x() {} } 불가능)



3. 예시

getter는 객체를 초기화 할 때 선언 할 수 있습니다.

var log = ['test'];
var obj = {
    get latest () {
        if (log.length == 0) return undefined;
        return log[log.length - 1]
    }
}
console.log(obj.latest); // Will return "test".

getter는 객체 초기화때 선언 할 수 있습니다.getter는 객체 초기화때 선언 할 수 있습니다.


getter는 삭제가 가능합니다.

var log = ['test'];
var obj = {
    get latest () {
        if (log.length == 0) return undefined;
        return log[log.length - 1]
    }
}
console.log(obj.latest);
delete obj.latest;
console.log(obj.latest);

getter는 삭제가 가능합니다.getter는 삭제가 가능합니다.


객체가 이미 존재 할 때, defineProperty 메소드로 getter를 정의할 수 있습니다.

var o = { a:0 }

Object.defineProperty(o, "b", { get: function () { return this.a + 1; } });

console.log(o.b) // Runs the getter, which yields a + 1 (which is 1)

객체가 이미 존재 할 때, defineProperty 메소드로 getter을 정의 할 수 있습니다.객체가 이미 존재 할 때, defineProperty 메소드로 getter을 정의 할 수 있습니다.


동적으로 getter 이름을 정의 할 수 있습니다.

이 방법은 ES6에서 포함된 기능입니다. 아직 여러 브라우저에서 지원되지 않고, 지원하지 않는 환경에서는 syntax error가 발생됩니다.

var expr = "foo";

var obj = {
    get [expr]() { return "bar"; }
};

console.log(obj.foo); // "bar"

동적으로 getter 이름을 정의 할 수 있습니다.동적으로 getter 이름을 정의 할 수 있습니다.



4. getter의 장점

계산 미루기 (Lazy getter)

getter는 프로퍼티에 접근하기 전까지 그 값을 계산하지 않습니다.

getter의 값 계산은 실제 값이 필요할 때 이루어지고, 값이 필요하지 않다면, 계산을 하지 않습니다. 즉 값이 필요하지 않다면 쓸데없는 계산을 하지 않아 cpu를 낭비하지 않게 됩니다.


캐싱 (Smart/Memorized getter)

최적화 방법으로 계산 미루기와 함께 캐싱하는 것이 있습니다.

값은 getter가 호출될 때 처음 계산되며 캐싱됩니다. 이후의 호출은 다시 계산하지 않고 이 캐시값을 반환합니다.


이러한 캐싱은 다음과 같은 경우에 유용합니다.

1. 값의 계산 비용이 큰 경우. (RAM이나 CPU 시간을 많이 소모하거나, worker thread를 생성하거나, 원격 파일을 불러오는 등)

2. 값이 당장 필요하지 않지만 나중에 사용되어야 할 경우(혹은 이용되지 않을 수도 있는 경우)

3. 값이 여러번 이용되지만 변경되지 않아 매번 계산할 필요가 없는 경우



5. getter 사용시 유의사항

getter은 값을 캐싱하고 있기 때문에 아래와 같은 경우, 사용에 유의해야 합니다.

var o = {
  set foo (val) {
    delete this.foo;
    this.foo = val;
  },
  get foo () {
    delete this.foo;
    return this.foo = 'something';
  }
};

o.foo = "test";
console.log(o.foo);

getter은 캐싱되기 때문에 유의해야 합니다.getter은 캐싱되기 때문에 유의해야 합니다.


something이 출력되어야 할 것 같지만. test가 출력되는 것을 확인 할 수 있습니다.



setter



setter는 객체의 프로퍼티를 설정하는 함수를 말합니다.



1. 문법

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


파라미터

prop

프로퍼티를 가져올 함수 이름


expression

ES6에서 추가되었습니다. 계산 되어지는 프로퍼티 이름을 위해 expression이 추가되었습니다. (getter와 방식이 동일 합니다.)



2. 설명

setter는 프로터티 값이 변경되어 질 때마다 함수를 실행하는데 사용됩니다.


setter를 사용할 때 3가지에 유의해야 합니다.

1. 식별자로 숫자와 문자를 모두 사용할 수 있습니다.

2. 한개의 파라미터만 가질 수 있습니다.

3. 리터럴 객체의 같은 이름의 set이나 동일한 이름의 프로퍼티를 가질 수 없습니다. ({ set x() {}, set x() {} }, { x : ..., set x() {} } 불가능)



3. 예시

setter는 객체를 초기화 할 때 선언 할 수 있습니다.

var o = {
    set current (str) {
        this.log[this.log.length] = str;
    },
    log: []
}
o.current = "test";
console.log(o);

setter는 객체 초기화때 선언 할 수 있습니다.setter는 객체 초기화때 선언 할 수 있습니다.


setter는 삭제가 가능합니다.

var o = {
    set current (str) {
        this.log[this.log.length] = str;
    },
    log: []
}
o.current = "test";
delete o.current;
o.current = "test2";

setter는 삭제가 가능합니다.setter는 삭제가 가능합니다.


delete로 setter가 삭제되어 test2가 저장되지 않는 것을 확인 할 수 있습니다.


객체가 이미 존재 할 때, defineProperty 메소드로 setter를 정의 할 수 있습니다.

var o = { a:0 };

Object.defineProperty(o, "b", { set: function (x) { this.a = x / 2; } });

o.b = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.a) // 5

객체가 이미 존재 할 때, defineProperty 메소드로 setter을 정의 할 수 있습니다.객체가 이미 존재 할 때, defineProperty 메소드로 setter을 정의 할 수 있습니다.


동적으로 setter 이름을 정의 할 수 있습니다.

getter와 동일하게, 이 방법은 ES6에서 포함된 기능입니다. 아직 여러 브라우저에서 지원되지 않고, 지원하지 않는 환경에서는 syntax error가 발생됩니다.

var expr = "foo";

var obj = {
  baz: "bar",
  set [expr](v) { this.baz = v; }
};

console.log(obj.baz); // "bar"
obj.foo = "baz";      // run the setter
console.log(obj.baz); // "baz"

동적으로 setter 이름을 정의 할 수 있습니다.동적으로 setter 이름을 정의 할 수 있습니다.


첫번째 console.log에는 초기값 bar가, 두번째 console.log에는 동적으로 이름되어진 foo로 정의된 baz가 출력되는 것을 확인 할 수 있습니다.



댓글
  • 프로필사진 swimyoung 정리해주신 글 잘 읽었습니다.
    도움이 많이 됐습니다.

    그런데, 아래 5번 chapter는 잘못된거 같아서 코멘트 남겼습니다.
    > 5. getter 사용시 유의사항

    MDN(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)에서 확인해보니 getter의 specification에는 getter가 값을 캐시하고 있다는 내용은 없습니다.
    > Smart / self-overwriting / lazy getters
    api문서에 위 부분이 있기는 하지만, getter를 이용해서 smart or memoized getters 를 할수 있다 정도의 예제인거로 저는 이해했습니다.

    그래서, 유의사항으로 들어주신 예제 코드를 잘 살펴봤는데요. 해당 예제 코드는 캐시와는 상관없이 setter에서 delete this.foo 동작을 수행하면서 this.foo의 getter와 setter가 삭제되서 해당 결과가 도출된거로 판단됩니다.
    ```
    // ...
    set foo (val) {
    delete this.foo // 이동작을 수행하면서, getter / setter 가 사라지게 됩니다.
    this.foo = val;
    }
    // ...

    // 정의해두었던 getter / setter가 setter함수에서 delete를 부르는 바람에 사라지게 됩니다.
    o.foo = 'test';

    // getter가 사라졌으니, getter는 당연히 불리지 않고 그래서 'test'가 출력됩니다.
    console.log(o.foo);
    ```

    혹시 제가 잘못 이해했거나, getter에 캐시관련해서 참고할만한 링크나 서적이 있으면 공유 부탁드립니다.

    다시 한번 공유하신 글 잘 읽었으며 감사드립니다. :)
    그럼, 좋은하루 되세요.
    2017.11.17 14:23
  • 프로필사진 버미노트 5. getter 사용시 유의사항에 예제는 getter의 캐시를 이야기하기에 맞지 않는 예인 것 같습니다.

    말씀해 주신 것과 같이 delete로 getter와 setter가 제거 된 후, o.foo = "test"로 선언된 test의 값이 출력되고 있는 것으로 보입니다.

    MDN에
    The value is calculated the first time the getter is called, and is then cached so subsequent accesses return the cached value without recalculating it. This is useful in the following situations
    이렇게 나와 있습니다.

    getter가 처음 호출될 때 계산 되어지고, 이후에는 캐시 된 값을 다시 계산하지 않고 반환한다고 합니다.

    이것을 표현할 수 있는 적절한 예제를 찾아 보았지만, 찾지 못하고 있습니다.
    2017.11.18 01:33 신고
  • 프로필사진 swimyoung 코멘트 달아주셔서 감사합니다.

    MDN 인용하신 문구는 getter에 specification이 아닌, getter를 사용한 technique(smart or memoized getters)을 이야기 하고 있는것 같습니다.
    > An additional optimization technique to lazify or delay the calculation of a property value and cache it for later access are smart or memoized getters. The value is calculated the first time the getter is called, and is then cached so subsequent accesses return the cached value without recalculating it. This is useful in the following situations:

    그래서 그 technique을 JS getter를 이용해서 구현한 예제가 MDN에 아래와 같이 들어있습니다.
    ```
    get notifier() {
    // 최초 notifier를 호출할때는 여기 getter가 호출됩니다.
    // 아래가 expensive한 계산을 하는 부분이고 이를 lazy하게 계산하고 다음번 호출시 다시 계산하지 않기 위해서 delete로 getter를 제거해줍니다.
    delete this.notifier;
    // 계산된 값을 notifier에 할당하면, 이제 계속 그 값을 사용하게 됩니다. (캐시? 처럼)
    return this.notifier = document.getElementById('bookmarked-notification-anchor');
    },
    ```

    ---

    저도 getter가 정말 cache 하는지에 대해서 specification 문서, examples 등을 찾아보고 테스트를 해봤는데, 찾지 못하고 캐시가되는것을 확인하지 못했습니다.
    2017.11.18 10:38
  • 프로필사진 비밀 var o = {
    _val: ' ',
    set foo (val) {
    this._val = val;
    },
    get foo () {
    delete this.foo;
    this.foo = 500;
    return this._val = "something";
    }
    };

    console.log(o._val); // ' '
    console.log(o.foo); // 'something'

    console.log(o._val); // 'something'
    console.log(o.foo); // '500'

    o.foo = "test";
    console.log(o._val); // 'something'
    console.log(o.foo); // 'test'

    ?? 스팸이라고 안 적히네요 ㅋㅋ
    2019.06.17 09:55
  • 프로필사진 비밀 아 윗 댓글 비밀번호 까먹어서 못 지우네요 ㅠ ㅠ 위에껀 삭제해주세요
    제가 이 글 보고 코딩하다가 기가막힌 상황에 딱 처해서 코딩을 하게 되었거든요.

      FMS.Windows = {};
      FMS.Windows._parameters = PluginManager.parameters('FMS_Windows');

      for(let key in FMS.Windows._parameters){
        if (/^\*+/.exec(key)) continue;
        Object.defineProperty(FMS.Windows, `_${key}`, {
          get: function(){
            delete this[`_${key}`];
            let value = Number(FMS.Windows._parameters[key]);
            if(Number.isNaN(value) || value === 0){
              value = FMS.Windows._parameters[key];
            }
            return this[`_${key}`] = value;
          },
          configurable: true
        });
      }
    이게 RMMV Plugin .js (javascript)에서 매개변수를 가져오는 부분인데요
    원래는 매개변수가 전부 문자열로
    key가 a인 애는 value "141"
    key가 b인 애는 value "539"
    key가 c인 애는 value "eee"

    이런 식으로 데이터를 받게 되는데
    실제로 쓸 때는 숫자들은 number 리터럴 변수로 쓰고 싶잖아요.

    이 때 각각의 key를 for in 구문으로 돌리면서 getter를 정의하는데
    만약에 이 매개변수들이 최초로 쓰이면 getter가 호출되고 연이어 delete getter되면서 단순 value를 가져오는 것으로 바뀌게 되는 거죠.

    그래서 한 번씩 전부 호출했다면
    숫자로 변환 가능한 문자열은 다음에 다시 호출될 때 문자열 처리 과정 없이 바로 number 변수처럼 가져와 쓰는데
    이게 게시글에 있는 캐싱이 아닐까 해요.

    시작하자마자 이루어지는 모든 매개변수의 처리 과정 없이 쓰이는 변수들만 동적으로 딱 1번 데이터 가공이 이루어지게 되죠.
    뭐 exec나 이런 건 매개변수가 *로 시작하는 경우엔 아예 정의를 안 한다던지 하는 부차적인 거구요.
    configurable: true여야 delete가 먹혀요.

    실제로 console.log에 찍어서 해당 객체가 리터럴 값으로 변하는 걸 확인했어요.
    2019.06.25 21:55
  • 프로필사진 버미노트 예시와 자세한 답변 감사드립니다 ^^ 2019.06.25 22:36 신고
댓글쓰기 폼