개요
es5의 Object.defineProperty를 이용해 특정 오브젝트의 키를 읽기전용으로 만드는 건 어렵지 않습니다만, 크로스브라우저 이슈 때문에 선뜻 쓰게 되지 않습니다.
IE의 경우 11은 거의 완벽한 es5머신이지만 10 이하는 호환성에 큰 문제가 있기 때문에 이를 고려하지 않을 수 없습니다.
따라서 보다 부드러운 폴리필을 처리해야 할 필요가 있습니다.
우선 날로 쓸 수는 없으니 어디에 폴리필용 함수를 정의할지 생각해봐야 합니다.
아무 객체의 아무 속성이나 읽기 전용으로 하기 위한 적당한 위치는 실로 다양합니다.
전역함수, Object의 커스텀 메소드 정의 등이 후보가 될 수 있습니다.
헌데 this컨텍스트의 사용을 용이하게 하기 위해 좀 과격하고 올드한 느낌이 있습니다만 Object.prototype에 직접 정의하는 방법을 쓸까합니다. 우선 이 방법을 익히고 나면 이후 다른 저장소에 구현하는 것은 문제가 되지 않을 것입니다.
함수작성
우선 Object.defineProperty 메소드가 존재한다면 읽기전용속성을 정의할 수 있다고 가정해도 좋겠죠(아닌 경우가 있긴 합니다만 >.<) 따라서 다음과 같은 사전 디텍팅을 정의합니다.
var es5 = Object.defineProperty ? 1 : 0;
이제 디텍팅 결과에 따라 반응하는 readOnly함수를 작성해보죠.
var readOnly = function(k, v){ if(es5){ //es5를 지원하는 경우 Object.defineProperty(this, k, {value:v}); }else{ //지원안하면 그냥 키에 쓴다. this[k] = v; } };
함수는 간단하게 es5를 지원하면 readonly로 설정하고 아닌 경우는 평소처럼 키를 설정해주는 식으로 폴리필합니다.
이제 이걸 Object.prototype에 넣어주면 끝이겠죠?
Object.prototype.readonly = readOnly;
으음 근데 뭔가 찝찝합니다.
Object.prototype의 키가 열거되는 문제
위의 코드를 사용하면 분명히 다음과 같은 코드가 바르게 작동합니다.
var obj = {}; //읽기전용 설정 obj.readonly('a', 3); console.log(obj.a); //3 obj.a = 5; //5로 바꿔보지만 console.log(obj.a); //여전히 3!
하지만 이 obj를 for in으로 루프를 돌면 readonly도 키에 걸려서 나옵니다.
var key; for(key in obj){ console.log(key); //a, readonly가 나온다! }
물론 prototype의 키이므로 hasOwnProperty를 사용하면 제거할 수 있습니다.
var key; for(key in obj){ if(obj.hasOwnProperty(key)){ console.log(key); //a만 나온다! } }
헌데 Object.prototype에 정의된 hasOwnProperty나 toString등은 안나오는데 readonly는 왜 열거되는 것일까요.
그것은 Object.prototype에 설정할 때 열거불가로 설정하지 않았기 때문입니다.
따라서 위에서 단순히 Object.prototype.readonly = readOnly 하던것도 defineProperty를 통해 잡아줘야합니다.
Object.defineProperty(Object.property, 'readonly', {value:readOnly});
이제서야 추가한 readonly도 for in에서 함부로 열거되지 않게 되었습니다!
응용하기
우선 폴리필의 전체 소스는 다음과 같습니다.
(function(){ var es5 = Object.defineProperty ? 1 : 0, readOnly = function (k, v){ es5 ? Object.defineProperty(this, k, {value:v}) : this[k] = v; }; if(es5){ Object.defineProperty(Object.prototype, 'readOnly', {value:readOnly}); }else{ Object.prototype.readOnly = readOnly; } })();
위 코드에서의 현재 극복하지 못하는 약점은 es5를 지원하지 않는 브라우저에서 readOnly가 열거되지 않게 할 방법이 없다는 것입니다.
(역시 hasOwnProperty는 생활화하는걸로 ^^)
이제 간단히 클래스를 하나 만들어서 생성자에서 받은 인자를 읽기전용으로 만들어보죠.
var TestClass = function(a){ this.readOnly('a', a); //a를 읽기전용으로 설정한다. }; var instance = new TestClass(3); console.log(instance.a); //3 instance.a = 5; //5로 바꿔보지만 console.log(instance.a); //여전히 3이다!
결론
이미 ECMA2015 즉 es6표준도 재정된 마당에 구형 브라우저 때문에 es5의 기능을 안쓸 수는 없는 노릇입니다.
작은 부분부터 폴리필하더라도 런타임 디버깅 대상을 줄이고 컨텍스트의 가정을 확신으로 바꿀 수 있는 장치가 있다면 적극적으로 활용할 노력을 해볼 때인가 싶습니다.
우선은 읽기전용 속성에 대해서 처리해봤지만 이러한 작은 es5폴리필을 꾸준히 연재할 예정입니다.
recent comment