[객체 프로퍼티 설정]
0. 프로퍼티 플래그와 설명자
프로퍼티는 키-값의 쌍으로만 다뤘으나 훨씬 유연한 자료구조이다.
- 프로퍼티 플래그 값 과 함께 가지는 flag 속성이 있다. 1) writable : true 이면 값을 수정 가능 2) enumerable : true 면 반복문으로 나열 가능 3) configurable : true 이면 프로퍼티 삭제 혹은 수정 가능
프로퍼티의 flag default 값은 모두 true, 언제든 수정이 가능하다.
-
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); 위와 같이 해당 프로퍼티의 정보를 얻을 수 있음 obj 객체명 propertyName 프로퍼티 명
- descriptor를 찍을 땐 JSON.stringify 로 찍어준다.
property descriptor: { "value": "John", "writable": true, "enumerable": true, "configurable": true }위와 같이 출력된다.
- Object.defineProperty(obj, propertyName, descriptor)
위를 사용하면 프로퍼티를 만들 수 있음
descriptor : 새롭게 만들 프로퍼티/플래그 정보
기존에 만드는 프로퍼티 생성법과 다르게 이 방법으로 만들면
플래그 정보가 모두 false로 만들어짐
Object.defineProperty(user, "name", { writable: false });이렇게 만들면 writable 을 false로 만들 수 있다.
- enumerable 플래그
열거가 불가능한 메서드가 있다. 대표적으로 toString()
ex)
let user = { name: "John", toString() { return this.name; } };열거가 불가능하다는 것은 for in 구문에 호출이 안된다는 것이다. 그런데 커스텀 toString을 추가하면 for in 구문에 toString이 나타난다.
- 플래그
-
- configurable 플래그
- 구성가능함을 나타내는 플래그
- configurable 플래그가 false로 설정되어 있으면 해당 프로퍼티는 객체에서 지울 수 없음 대표적으로 내장 객체 Math의 PI (파이를 나타냄) 쓰기 열거, 구성이 불가함. configurable 플래그를 false로 설정하면 다시 바꿀 수 없음 (defineProperty 를 사용해도 불가함)
- configurable : false에 따른 제약사항 1) configurable 플래그를 수정할 수 없음 2) enumerable 플래그를 수정할 수 없음. 3) writable: false의 값을 true로 바꿀 수 없음(true를 false로 변경하는 것은 가능함). 4) 접근자 프로퍼티 get/set을 변경할 수 없음(새롭게 만드는 것은 가능함). => 영원히 변경할 수 없는 프로퍼티를 만들 때 사용한다.
-
- defineProperties 를 사용하면 여러개의 프로퍼티의 플래그 여러개를 한번에 정의 가능
- getOwnPropertyDescriptor(obj) 이렇게 사용하면 obj(객체)의 프로퍼티를 가져올 수 있다.
- defineProperties({새로만들 객체}, getOwnPropertyDescriptor(obj)) 이렇게 사용하면 플래그까지 복사할 수 있는 것이다.
- for in 을 사용하여 객체 복사 하면 프로퍼티의 플래그까지 복사되지는 않는 문제가 있음
- 프로퍼티 getter와 setter 프로퍼티에는 두종류가 있다. 1) 데이터 프로퍼티 : 프로퍼티에 값이 할당되는 경우, 지금까지 공부하면서 본 모든 프로퍼티 2) 접근자 프로퍼티 : 본질은 함수, 값을 획득(get) 설정(set) 하는 역할 . 그런데 외부에서는 함수가 아닌 프로퍼티처럼 보임
접근자 프로퍼티 코드 예시)
let obj = {
get propName() {
// getter, obj.propName을 실행할 때 실행되는 코드
},
set propName(value) {
// setter, obj.propName = value를 실행할 때 실행되는 코드
}
};
getter 메서드 : obj.propName을 실행할 때 실행 setter 메서드 : obj.propName = value 처럼 값을 할당할 때 실행됨
get 은 그냥 쓰면 되고 set에 대한 코드만 보면..
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
위와 같이 쓴다.
obj.fullName = "Alice Cooper" 라고 코딩했다면
name 에 Alice surname 에 Cooper 가 입력된다.
-
- 접근자 프로퍼티의 설명자는 writable, value는 없음
- get, set, enumerable, configurable 이렇게 되어있음 그러므로 프로퍼티 설정에서 get과 value를 같이 쓸수 없다.
[프로토타입과 프로토타입 상속]
-
프로토 타입 상속 객체지향 언어에서 나오는 상속의 개념과 유사한 듯 하다. user라는 객체가 있는데 이 객체의 속성을 그대로 가져가면서 속성이 추가되는 admin이나 guest가 있을 경우 프로토타입 상속이란 것을 해야한단다.
- [[prototype]] 객체는 [[prototype]]라는 숨김프로퍼티를 갖는다 (옛날 함수에서 렉시컬 환경을 갖는것과 유사한 개념이다.) [[prototype]] 은 다른 객체에 대한 참조를 프로퍼티 값으로 가진다. 그 참조 대상을 “프로토 타입” 이라고 부른다.
-
프로토타입의 동작 : 객체의 프로퍼티에서 값을 찾다가 못찾으면 프로토타입에서 찾게됨
- 프로토타입은 숨김 프로퍼티이지만 값을 설정할 수 있다. “__proto__” 사용
코드 예시
let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal; // rabbit 의 프로토타입을 animal로 설정 - 프로토타입 체인 animal <= rabbit <= lonEar 이런 식으로 상속할 수 있다. 프로토타입 체이닝에 제약조건 1) 순환 참조(circular reference)는 허용되지 않음. __proto__를 이용해 “닫힌 형태”로 다른 객체 참조시 에러 발생 2) __proto__의 값은 객체나 null만 가능, 다른 자료형은 무시
-
객체엔 오직 하나의 [[Portotype]]만 있을 수 있다. 객체는 두 개의 객체를 상속받지 못함.
-
write 시 프로토타입 anmial(prototype) : eats 가 있고 => rabbit(상속) : eats를 만들어 정의한 경우 rabbit의 eats가 우선 호출이다.
- 프로토타입과 ‘this’
```javascript
// animal엔 다양한 메서드가 있습니다.
let animal = {
walk() {
if (!this.isSleeping) {
alert(
동물이 걸어갑니다.); } }, sleep() { this.isSleeping = true; } };
let rabbit = { name: “하얀 토끼”, proto: animal };
// rabbit의 프로퍼티 isSleeping을 true로 변경합니다. rabbit.sleep();
alert(rabbit.isSleeping); // true alert(animal.isSleeping); // undefined (프로토타입에는 isSleeping이라는 프로퍼티가 없습니다.)
위의 코드에서
animal => rabbit 의 상속 체인에서
rabbit 에서 sleep 호출 시 this.isSleeping 은 rabbti에서 만들어진다고 봐야한다.
메서드의 경우는 호출하여 사용하는 느낌이고 그 안의 this.value는 해당 객체 안에서 만들어지는 개념이다.
- 상속과 for in
Object.keys(obj)로 객체의 키값을 검색하면 해당 객체의 키 값만 검색되지만
for in 구문으로 객체를 돌 때는 상속해주는 프로토타입의 키까지 모두 순회한다.
obj.hasOwnProperty(key)를 사용하면 상속된 프로퍼티 말고 해당 객체에서만 만들어진 프로퍼티일 때만 true로 반환한다.
3. 함수의 prototype 프로퍼티
위의 내용을 하기 전에...
먼저 "객체:기본" 에서 배웠던 new 연산자를 한번 더 집고 넘어가야할 필요성을 느꼈다.
3-1. new 연산자
- 생성자 함수
객체를 만들 때 보통은 {...} 이런식으로 "리터럴 방식"을 사용하는 것이 일반적이다.
그런데 개발하다보면, 유사한 객체를 여러개 만들어야 할 때가 생긴다.
그 때 사용하는 것이 new 연산자, 생성자 함수이다.
* 생성자 함수의 관례
1) 함수 이름의 첫 글자는 대문자로 시작
2) 반드시 "new" 연산자를 붙여 실행
예시
```javascript
let user = new User("Jack");
function User(name){
this.name = name;
}
위의 함수에서 new 연산자를 쓰면 this라는 애가 위에 생기는 것과 마찬가지란다.
function User(name){
// this = {};
this.name = name;
// return this;
}
위의 주석과 같이 동작하는 것이다.
3-2. 함수의 프로토타입 프로퍼티
//아래_코드_참조
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
Rabbit 함수 를 new 연산자를 써서 rabbit이라는 객체가 생성된다.
let rabbit{
name : "White Rabbit";
prototype : "animal";
}
위와 같이 생성되는 것이다.
3-3. 함수의 프로토타입 프로퍼티와 constructor 프로퍼티 함수의 프로토타입 프로퍼티의 기본 값은 constructor이다. 아래와 같다.
function Rabbit() {}
// 기본 prototype:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
만약…
let rabbit = new Rabbit();
이렇게 객체를 하나 만들었다면… rabbit의 프로토타입 프로퍼티로 constructor를 사용할 수 있다. 그러므로..
rabbit.constructor == Rabbit
이 되는 것이다.
그러면 이런 사용도 가능하다.
let rabbit2 = rabbit.constructor();
이렇게 기존의 객체를 가지고 다른 객체를 하나 만드는 것도 가능하다.
그러나 constructor는 그냥 프로퍼티일 뿐이다. 만약 함수의 프로토타입 프로퍼티를 딴 값으로 설정했다면 constructor를 못쓰는 것이다. 프로토타입 프로퍼티 안에 프로퍼티를 넣어서 만들수는 있다.
- 네이티브 프로토타입 내장 생성자 함수에서 프로토타입 프로퍼티를 사용한다.
-
Object.prototype let obj = {}; alert( obj ); // "[object Object]" ?위의 코드에서 “[object Object]” 를 찍어주는 코드는 도데체 어디에 있는가.
let obj = {}; //의 의미는
obj = new Object(); //와 같다.
Object란 객체를 만들어주는 함수인 것이다. 근데 이 Object의 프로토타입이 있는데 거대한 객체이다.
그래서 obj.toString() 을 호출할 때 Object.prototype 에서 가져온다.
- 네이티브 프로토타입 조작하기 ```javascript String.prototype.show = function() { alert(this); };
“BOOM!”.show(); // BOOM!
String 타입의 프로토타입으로 접근 한 후 메서드를 추가하면 맘대로 쓸 수도 있다.
하지만 프로토타입은 전역으로 영향을 끼치기에 그렇게 좋은 방법은 아니다.
폴리필을 만들 때 쓰인다.
폴리필 : 자바스크립트 명세서에 있는 메서드와 동일한 기능을 하는 메서드 구현체.
명세서에는 정의되어 있으나 특정 자바스크립트 엔진에서는 해당 기능이 구현되어있지 않을 때 사용
- 프로토타입에서 빌려오기
```javascript
let obj = {
0: "Hello",
1: "world!",
length: 2,
};
obj.join = Array.prototype.join;
alert( obj.join(',') ); // Hello,world!
위의 경우 obj라는 객체에서 Array.prototype 안의 join 기능을 사용하기 위해 빌려온 것이다.
- 모던한 프로토타입 사용법
- Object.create(obj) obj를 프로토 타입으로 설정
- Object.getPrototypeOf(obj) obj의 프로토타입을 가져옴
- Object.setPrototypeOf(obj, {}) 프로토 타입을 변경함.