-
[JS/Object] 자바스크립트, 객체의 프로퍼티(Property)에 대하여 (Object.defineProperty()와 getter, setter사용하기)Frontend 2019. 7. 31. 14:17
Object
자바스크립트의 Object, 즉 객체는 여러가지의 자료(Data)들과 함수(Function)들의 집합이다. 자바스크립트에서는 원시 데이터 타입인 Number, String, Boolean, Null, Undefined를 제외한 모든 것들을 다 Object type이라고 한다. 함수도, 배열도 모두 Object라는 큰 범주 안에 속한다.
const dessert = { dessertName: 'chocolate cake', sweetness: 6, ingredient: ['flour', 'eggs', 'chocolate', 'sugar', 'butter'] };
객체는 new Object()나 Object.create(), 혹은 literal notation을 사용하여 초기화될 수 있다.
위와 같이 쓰여진 객체가 바로 literal notation으로 쓰여진 형태를 말한다.
객체 초기자(Object initializer)는 0개 이상의 속성명과 그에 해당하는 값의 목록이 {}(중괄호)에 묶여있다.
const party = { host: 'Tom', eventName: 'birthday party', time: '7pm', location: 'ABC restaurant', invited: ['Sally', 'Ann', 'Paul', 'Andrew'], invitation: function () { return `You are invited to ${this.host}'s ${this.eventName}!`; } }; console.log(party.invitation()); // You are invited to Tom's birthday party!
객체의 값에는 원시 데이터 타입 뿐만 아니라 배열, 객체, 함수가 들어갈 수도 있다.
객체 내부에 함수를 쓰고 그 안에서 this를 사용한다면 this는 그 함수가 존재하는 객체 자체를 가리킨다.
party 객체 코드를 보면 객체 안에 host, eventName 등의 값들이 저장된 것처럼 보이지만 사실은 저 property들은 실제 값이 저장된 메모리의 주소를 가리키고 있을 뿐이다. 만약 우리가 party.host를 'Jane'으로 바꾼다면 그것은 host라는 프로퍼티가 가리키고 있는 메모리 공간에서 이루어진다.
Object.defineProperty()
const dessert = { dessertName: 'chocolate cake', sweetness: 6, ingredient: ['flour', 'eggs', 'chocolate', 'sugar', 'butter'] }; dessert.price = '20$';
만약에 dessert 객체에 새로운 프로퍼티를 추가하고 싶으면 위와같이 할당 연산자를 사용해서 간단하게 추가할 수 있다. 만약에 위 코드처럼 값을 할당하여 속성을 추가하는 일반적인 방법을 사용하면 자동으로 enumerable과 configurable, writable 속성이 true로 설정된다.
즉, enumerable: true이기 때문에 값을 for...in문 등의 방법으로 열거할 수 있고,
configurable: true이기 때문에 delete 등을 사용하여 속성을 삭제할 수 있고,
writable: true이기 때문에 추후에 할당 연산자로 값을 변경할 수 있다.
const dessert = { dessertName: 'chocolate cake', sweetness: 6, ingredient: ['flour', 'eggs', 'chocolate', 'sugar', 'butter'] }; Object.defineProperty(dessert, 'price', { value: '20$' });
Object.defineProperty()는 객체에 직접 새로운 속성을 정의하거나 이미 존재하는 속성을 수정한 후 그 객체를 반환한다. defineProperty() 메소드를 사용하면 enumerable, configurable 등의 속성을 상세하게 조절할 수 있고, 값을 getter나 setter를 사용하여 다른 객체나 변수에서 읽어올 수도 있다.
Object.defineProperty(obj, prop, descriptor);
Object.defineProperty() 메소드는 3개의 인자를 받는다.
- obj: 프로퍼티를 정의할 대상 객체,
- prop: 새로 정의하거나 수정하려는 프로퍼티의 이름,
- descriptor: 새로 정의하거나 수정하려는 속성을 쓰는 객체이다.
여기서 속성 서술자(property descriptors)는 데이터 서술자(data descriptors)와 접근자 서술자(accessor descriptors)로 나뉜다. 데이터 서술자는 값을 가지는 속성이고, 접근자 서술자는 접근자(getter)와 서술자(setter) 한 쌍을 가지는 속성이다.
* 데이터 서술자(data descriptors)와 접근자 서술자(accessor descriptors)는 모두 가지는 키:
configurable 이 속성의 값을 변경할 수 있고, 대상 객체에서 삭제할 수도 있다면 true. 기본값은 false. enumerable 이 속성이 대상 객체의 속성 열거 시 노출된다면 true. 기본값은 false. * 데이터 서술자(data descriptors)만 가지는 키:
value 속성에 연관된 값. 아무 유효한 JavaScript 값(숫자, 객체, 함수 등)이나 가능.
기본값은 undefined.writable 할당 연산자로 속성의 값을 바꿀 수 있다면 true.
기본값은 false.* 접근자 서술자(accessor descriptors)만 가지는 키:
get 속성 접근자로 사용할 함수, 접근자가 없다면 undefined. 속성에 접근하면 이 함수를 매개변수 없이 호출하고, 그 반환값이 속성의 값이 된다. 이 때 this 값은 이 속성을 가진 객체(상속으로 인해 원래 정의한 객체가 아닐 수 있음)이다.
기본값은 undefined.set 속성 설정자로 사용할 함수, 설정자가 없다면 undefined. 속성에 값을 할당하면 이 함수를 하나의 매개변수(할당하려는 값)로 호출한다. 이 때 this 값은 이 속성을 가진 객체이다.
기본값은 undefined.
위 내용은 MDN에도 적혀있으므로 자세한 설명은 생략하고 예제 코드를 살펴보자.
const dessert = { dessertName: 'chocolate cake', sweetness: 6, ingredient: ['flour', 'eggs', 'chocolate', 'sugar', 'butter'] }; Object.defineProperty(dessert, 'price', { value: '20$' });
위의 price 속성은 value 속성을 가지므로 데이터 서술자(data descriptors)를 가지게된다.
이 때 price 속성은 configurable과 enumerable 키가 기본값인 false로 되어있으므로 값을 삭제할 수 없고, for...in문이다 Object.keys() 등으로 접근하여 순회하는 것도 불가능하다. 또한 writable 키도 기본값 false로 되어있으므로 할당 연산자로 값을 수정할 수도 없다.
Object.defineProperty(dessert, 'price', { value: '20$', enumerable: true, configurable: true, writable: true });
이제 enumerable 키와 configurable, writable 키를 모두 true로 설정하였다.
이제 할당 연산자로 값을 수정할 수도 있고 for...in문으로 해당 프로퍼티에 접근할 수도 있고
delete로 해당 프로퍼티를 삭제할 수도 있다.
이 것이 바로 데이터 서술자이다. 말그대로 프로퍼티가 데이터의 값을 가지고 있는 것이다.
반면에 접근자 서술자는 접근자(getter)-서술자(setter) 함수를 실행하여 함수의 리턴 값을 가져온다.
const dessert = { dessertName: 'chocolate cake', sweetness: 6, ingredient: ['flour', 'eggs', 'chocolate', 'sugar', 'butter'] }; let cakePrice = '20$'; Object.defineProperty(dessert, 'price', { enumerable: true, configurable: true, get () { return cakePrice; }, set (newValue) { cakePrice = newValue; } });
위 코드를 보면 price 속성을 정의할때 get 함수를 사용하여 전역 공간에 있는 cakePrice 변수를 리턴하라고 되어있다.
또한 set 함수를 통해 cakePrice 변수를 새로운 값으로 할당하도록 되어있다.
위 dessert 객체를 콘솔 창에서 살펴보면 price라는 속성과 함께, price 속성에 대한 get, set 함수가 생겨난 것을 확인할 수 있다. 이 코드에서 만약에 dessert.price 값을 읽으려고 하면 get 함수를 실행하게 되고, get 함수의 리턴 값인 cakePrice 값을 가져오게된다.
만약에 dessert.price = '10$'; 라고 설정하면, 이번엔 set 함수가 실행되고 변경된 value인 '10$'가 set 함수의 인자로 들어가게 된다. 그러면 cakePrice 변수의 값이 '10$'로 바뀌게 되고 후에 다시 dessert.price에 접근하면 '10$'로 변경된 cakePrice가 출력된다.
function Dessert (name, sweetness) { const priceLog = ['20$']; let cakePrice = '20$'; this.dessertName = name; this.sweetness = sweetness; Object.defineProperty(this, 'price', { enumerable: true, get () { return cakePrice; }, set (newPrice) { cakePrice = newPrice; priceLog.push(newPrice); } }); this.getPriceLog = function () { return priceLog; } } const cake1 = new Dessert('lemon cake', 8);
이제 get과 set을 사용하여 다양한 일들을 할 수 있다.
Dessert라는 생성자 함수에 배열을 만들고 dessert의 price 값이 변경될 때마다
price값을 배열에 저장하여 바뀐 값들을 기록할 수도 있다.
const party = { host: 'Tom', eventName: 'birthday party', time: '7pm', location: 'ABC restaurant', invited: ['Sally', 'Ann', 'Paul', 'Andrew'], invitation: function () { return `You are invited to ${this.host}'s ${this.eventName}!`; } }; function Dessert (occasion, dessertName, sweetness) { this.dessertName = dessertName; this.sweetness = sweetness; for (let prop in occasion) { if (occasion.hasOwnProperty(prop)) { Object.defineProperty(this, prop, { configurable: true, enumerable: true, get: function () { return occasion[prop]; }, set: function (changedValue) { occasion[prop] = changedValue; } }); } } } const bdayParty = new Dessert(party, 'chocolate cake', 6);
이렇게 생성자 함수에 다른 객체를 인자로 받아와서 속성으로 추가할 수도 있다.
bdayParty.invited.push('Elen'); console.log(party.invited); console.log(bdayParty.invited);
Dessert 객체에서 생성한 인스턴스인 bdayPraty.invited에 'Elen'이란 값을 push하면
bdayParty 인스턴스는 invited 값을 party 객체에서 읽어오고, party 객체의 invited 배열에 'Elen'을 push하게 된다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
반응형'Frontend' 카테고리의 다른 글
COMMENT