728x90

본 강의는 자바스크립트의 기초를 대충 안다고 가정하고 시작하는 조금 심도 깊은 포스팅이다.

완전 처음부터 배우고 싶다면 다른 블로그나 책의 글을 참조하기를 바란다.

특별한 추가 설명이 없다면 nodejs가 아닌 브라우저에서 사용하는 js를 의미한다.


참고:

javascript docs

ecmascript specification


개발하는 입장에서 봤을 때 자바스크립트는 좀 불완전하다는 느낌을 받는다.

왜냐하면 다른 객체지향과 좀 많이 다르기 때문이다.

접근자도 없지, 설정자도 없지, 캡슐화는 엉성하지... 과거에는 그랬다.

그러나 새로운 버전이 추가되면서 Object.defineProperty라는 새로운 기술이 추가됬다.

이는 오히려 높은 수준의 캡슐화를 제공해주는, 사용하려면 더 좋은 기술이다.

그리고 이 기술은 vue의 근간 기술이기도 하다.


이 기술에 대해서 알아보기 전에 먼저 그냥 속성을 추가하는 예제를 한번 사용하도록 해보자.


let obj = {'name': 'kukaro'};
obj['age'] = 27;

console.log(obj);

위의 예제는 우리가 여러번 사용했던 일반적인 예제이다.

사실 너무 일반적이라서 특별히 뭐라 할 것도 없다.

이를 실행한다면 당연히도 아래와 같은 결과가 나온다.


우리가 흔히 보는 일반적은 결과이다.

그러면 이제 Object.defineProperty를 통해서 한번 선언하도록 해보자.


let obj = {'name': 'kukaro'};
obj['age'] = 27;
Object.defineProperty(obj, 'alias', {
value: 'jiharu',
});
console.log(obj);

사용하는 모습이 정말 복잡하다.

이렇게 사용하려면 사용하는 맛이 안나는데 뭐 어쩔수 없다.

위의 예제는 obj에 alias라는 새로운 속성을 추가했다.

형식을 정형화해서 보자면 아래와 같다.


Object.defineProperty(<적용할 Object>, <속성명>, <설정값>)


정말... 이렇게 까지 해야하나 싶다.

그래서 사실 놀라운작업(자신의 기준으로)을 할때나 주로 쓰인다.

일단 이렇게 쓰게되면 몇가지 차이점이 존재하는데 먼저 출력값을 보자.


자세히 보면 우리가 선언한 속성값이 연보라색으로 보인다.

여러분은 이때까지 이걸 보면서 무슨생각을 했는가?

별 대수롭지 않게 넘긴게 대부분일것 같다.(그냥 상속했나보지 뭐 이런식으로?)

그런데 사실 이 연보라색의 의미는 해당값이 enumerable하지 않다는 뜻이다.


작업을 몇개더 추가해보면 더 정확하게 알 수 있다.


let obj = {'name': 'kukaro'};
obj['age'] = 27;
Object.defineProperty(obj, 'alias', {
value: 'jiharu',
});

delete obj['alias'];
console.log(obj);

오브젝트를 아예 제거하고 싶을때는 우리는 delete 키워드를 사용한다.

과연 지워질까???

물론 안지워지니까 하는 말이다.

결과 볼필요도 없다.

그냥 아무 일도 일어나지 않는다.

위의 작업은 사실 명시적으로 보면 아래와 같이 동작한다.


Object.defineProperty(obj, 'alias', {
value: 'jiharu',
configurable: false,
enumerable: false,
writable: false,
});

뭐가 죄다 안된단다.

하나하나를 알아보기전에 원래 소스코드를보자.


interface PropertyDescriptor {
configurable?: boolean;
enumerable?: boolean;
value?: any;
writable?: boolean;
get?(): any;
set?(v: any): void;
}

타입스크립트 소스코드이므로 이해할려고 노력할 필요는 없다.

다만 속성의 갯수와 각각이 무엇을 의미하는지를 이제부터 알 차례이다.

괄호는 디폴트 값이다.


configurable(false) - 정의 할수 있음을 의미, 속성의 설정값을 재정의 하거나 속성을 지우는 것이 가능해진다.

writable(false) - 속성을 쓰는것이 가능해진다. 다만 지우는 것과는 연관이 없다. get,set을 지정할 경우 같이 사용 불가

value(undefiend) - 값을 지정해서 쓴다. undefiend를 위해 쓰는 사람은 없으므로 사실상 필수값, 다만 get,set을 지정할 경우 같이 사용 불가

enumerable(false) - 해당 값을 사용하면 Object.keys나 for in구문에 노출되서 나온다.

get(undefiend) - 접근자를 세팅한다. 속성에 값을 가져올 때 이제 이 함수를 거쳐서 사용한다.

set(undefiend) - 설정자를 세팅한다. 속성에 값을 쓸 때 이제 이 함수를 거쳐서 사용한다.


하나하나 확인해보도록하자.


configurable : 속성 삭제 불가, 속성 설정 불가


let obj1 = {'name': 'kukaro'};
let obj2 = {'name': 'jiharu'};

Object.defineProperty(obj1, 'age', {
value: 27,
configurable: true,
});

Object.defineProperty(obj2, 'age', {
value: 23,
});

try {
Object.defineProperty(obj1, 'age', {
value: 127,
});
Object.defineProperty(obj2, 'age', {
value: 123,
});
} catch (e) {
console.log(e);
}

console.log(obj1);
console.log(obj2);

속성을 삭제할 수 없다는건 위의 예시에서 나왔으므로 넘어가도록하겠다.

설정할수 없다는건 defineProeprty를 통해서 다시 설정할 수 없다는 뜻이다.

만약 여러분이 다시 설정하려면 js답지 않게(사실 이게 정상임) Error를 내뿜게 되므로 조심해야한다.


값의 변동조차 거부한다. strict모드와 상관이 없다.


writable : 쓰기 불가


let obj1 = {'name': 'kukaro'};
let obj2 = {'name': 'jiharu'};

Object.defineProperty(obj1, 'age', {
value: 27,
writable: true,
});

Object.defineProperty(obj2, 'age', {
value: 23,
});

obj1.age = 127;
obj2.age = 123;

console.log(obj1);
console.log(obj2);

위의 코드처럼 값을 덮어쓰려고할때 writable이 true여야만 덮어 쓸 수 있다.


보다시피 값이 한쪽은 덮어쓰는게 가능하고 한쪽은 불가능하다는걸 알 수 있다.


enumerable : for in이나 Object.keys의 나열 불가


let obj1 = {'name': 'kukaro'};
let obj2 = {'name': 'jiharu'};

Object.defineProperty(obj1, 'age', {
value: 27,
enumerable: true,
});

Object.defineProperty(obj2, 'age', {
value: 23,
});

console.log(obj1);
console.log(obj2);
console.log(Object.keys(obj1));
console.log(Object.keys(obj2));



나열 불가가 되면 접근은 가능하지만 Object.keys나 for in구문으로의 나열이 불가능하게된다.

그리고 잘보면 원소색이 연보라색에서 보라색으로 돌아왔다.


get과 set : 접근자와 설정자


let obj1 = {'name': 'kukaro'};
let obj2 = {'name': 'jiharu'};

Object.defineProperty(obj1, 'age', {
get: function () {
console.log('obj1 접근자');
return value;
},
set: function (newValue) {
console.log('obj1 설정자');
value = newValue;
}
});

Object.defineProperty(obj2, 'age', {
writable: true,
configurable: true
});

obj1.age = 27;
obj2.age = 23;

console.log(obj1);
console.log(obj2);
console.log(obj1.age);
console.log(obj2.age);

접근자와 설정자는 우리가 흔히 아는 그 접근자와 설정자 맞다.

다른 모던한 언어들(자바 말하는거아님)과 마찬가지로 꽤 모던하게 설계됬다.

이 설정자를 만들때는 value와 writable을 선언하면 안된다.

선언하면 에러를 내뱉기 때문이다.


설정자와 접근자로 선언하면 호출시에 해당 함수를 통해서 호출하게된다.

그래서 예비 작업을 할 수 있으므로 매우 편하게 사용할 수 있다.

+ Recent posts