객체 프로퍼티에는 데이터 프로퍼티와 접근자 프로퍼티 두 가지가 있다. 접근자 프로퍼티는 메서드와 비슷한데, 1. getter와 setter 두 가지 함수로 구성된 점과 2.접근했을때 함수라기보단 데이터 프로퍼티와 비슷하게 동작한다는 점에서 조금 다르다. 이런 점에서 접근자 프로퍼티를 동적 프로퍼티라고 부르기도 한다.
// setEmail과 getEmail메서드가 있다. //@가 들어있는 문자열은 모두 유효한 이메일 주소라고 간주 const USER_EMAIL = Symbol(); classUser{ setEmail(value) { if (!/@/.test(value)) thrownewError(`invalid email ${value}`); this[USER_EMAIL] = value; } getEmail() { returnthis[USER_EMAIL]; } }
//이 클래스는 다음과 같은 방식으로 사용해야함 const u = new User(); u.setEmail("join@dot.com"); console.log(`${u.getEmail()}`);
//그러나 우리는 이 방식을 더 자연스럽게 느낌 const u = new User(); u.email = "join@dot.com"; console.log(`${u.getEmail()}`);
USER_EMAIL 프로퍼티 대신 두 가지 메서드(setEmail,getEmail)을 쓴 이유는 잘못된 이메일 주소가 저장되는 것을 방지하려고함.
프로퍼티에는 심볼을 서 실수로 직접 접근하는 것을 방지
접근차 프로퍼티를 사용하면 후자의 자연스러운 문법을 사용하면서도, 부주의한 접근을 차단하는 전자의 장점을 누릴수 있다.
접근자 프로퍼티를 사용하여 이메일 체크
1 2 3 4 5 6 7 8 9 10 11
//접근자 프로퍼티를 사용하여 이메일 체크 const USER_EMAIL = Symbol(); classUser{ set email(value) { if (!/@/.test(value)) thrownewError(`invalid email ${value}`); this[USER_EMAIL] = value; } get email(value){ returnthis[USER_EMAIL]; } }
함수 두 개(set,get)을 사용했지만 두 함수는 email 프로퍼티 하나에 묶였다.
프로퍼티를 할당할때는 setter 가 호출되고, 할당하는 값이 첫 번째 매개변수로 전달된다. 프로퍼티를 평가할 때는 getter가 호출된다.
setter없이 getter만 만들수도 있다.
프로퍼티에는 항상 키가 있고, 키는 문자열이나 심볼일 수 있다. 프로퍼티의 값은 어떤 타입이든 괜찮다. 배열이나 맵과 달리 객체의 프로퍼티에는 순서가 없다. 객체 프로퍼티에 접근 할 떄는 점(.)연산자나 대괄호 연산자([])를 사용한다. 객체 프로퍼티는 식별자를 키로 사용하는 일반적인 프로퍼티, 심볼이나 표현식을 사용하는 계산된 프로퍼티, 메서드 단축 표기의 세 가지가 있다. 프로퍼티에는 자신이 속한 객체 안에서 어떻게 동작할지 결정하는 속성이 있다. 우리가 아는 방식으로 프로퍼티를 만들고, Object.getOwnPropertyDescriptor을 서서 속성을 알아보자.
Object.defineProperty로 데이터 프로퍼티를 추가 할 때는 value프로퍼티를 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12
//obj에 name 과 greet 프로퍼티를 추가해보기 Object.defineProperty(obj, "name", { value: "cynthia" }); Object.defineProperty(obj, "greet", { value: function() { return`hello my name is ${obj.name}`; } });
console.log(obj.name); //cynthia console.log(obj.greet()); //hello my name is cynthia
Object.defineProperty는 배열 프로퍼티를 나열할 수 없게 만들때 주로 사용한다. 배열은 원래 프로퍼티를 사용하지 않도록 설계되었으므로 문자열이나 심볼 프로퍼티는 사용하지 않는 편이 좋다. 배열에서 for…in이나 Object.keys를 사용하는 것 역시 권장하지 않는다. 따라서 배열에 숫자형 프로퍼티가 아닌 다른 프로퍼티를 추가한다면 , 그 배열에서 for…in이나 Object.keys를 사용했을때 노출되지 않도록 나열할 수 없게 만들어야 함
자바스크립트에는 객체를 보호해서 의도하지 않은 수정을 막고, 의도적인 공격은 더 어렵게 만드는 세 가지 메커니즘이 있다. 동결(freezing),봉인(sealing),확장금지(preventing extension)이다.
객체동결 : 동결된 객체는 수정할 방법이 없다. 일단 객체를 동결하면 다음과 같은 작업이 불가능해진다. 객체를 동결하면 그 객체는 문자열이나 숫자처럼 불변이된다. Object.freeze를 사용하고, 객체가 동결됐는지 확인 할 때는 Object.isFrozen을 사용한다.
객체 봉인 : 객체를 봉인하면 새 프로퍼티를 추가하거나 기존 프로퍼티를 변경 , 삭제할 수 없습니다. (프로퍼티의 값을 변경하는 것은 가능) 클래스의 인스턴스를 사용하면서, 인스턴스의 프로퍼티를 수정하는 메서드가 동작하도록 할때 봉인을 사용할 수 있습니다. Object.seal로 객체를 봉인하고, 확인할때는 Object.isSealed를 사용한다.
확장 금지 : 객체에 새 프로퍼티를 추가하는 것만 금지된다. 프로퍼티에 값을 할당하거나 , 삭제하거나, 속성을 변경하는 작업은 모두 허용된다. 확장을 금지할땐 Object.preventExtensions, 확장 금지를 확인할때는 Object.isExtensible을 사용한다.
//betterCofficients 객체의 프락시에는 무한한 프로퍼티가 있고, //직접 정의한 프로퍼티를 제외하면 값이 0인것과 마찬가지이다. betterCofficients.a; //1 betterCofficients.b; //0 betterCofficients.c; //5 betterCofficients.d; //0
//키로 소문자 한 글자만 받았을때 프락시가 동작하게 할 수도 있다. const betterCofficients = newProxy(cofficients, { get(target, key) { if (!/^[a-z]&/.text(key)) return target[key]; return target[key] || 0; } });
Proxy 생성자에 넘기는 첫 번째 매개변수는 타켓, 즉 프락시 할 객체이다. 두번째 매게변수는 가로챌 동작을 가리키는 핸들러이다. 여기서 프로퍼티에 접근하는 동작만 가로챘으며, get 함수가 핸들러이다. get 함수는 매개변수로 타켓, 프로퍼티키(문자열 또는 심볼), 수신자(프락시 자체 또는 프락시에서 파생되는 것)을 받는다. 해당 키가 타켓에 있는지 확인하고, 없으면 o을 반환한다.