본 게시물은 Effective Javascript의 내용을 재구성하여 작성되었음을 알립니다. 저작권 문제 발생시 게시물이 비공개 될 수 있습니다.
- with 선언문을 사용하지 마라.
- 객체로의 반복되는 접근을 위해 짧은 변수 이름을 사용하라
- with 선언문으로 암묵적으로 바인딩하는 대신에 명시적으로 지역 변수를 객체 프로퍼티에 바인딩하라.
with를 사용하지 마라 (46p)
with는 하나의 객체에서 여러 메서드를 호출할 때 해당 객체에 대함 참조를 반복할 필요가 없는 경우 사용이 편리하다.
function status(info) {
var widget = new Widget();
with (widget) {
setBackground("blue");
setforeground("white");
setText("Status: " + info); // 모호한 참조
show();
}
}
모듈로 제공되는 객체의 변수를 불러들이기 위해 with를 사용하기도 한다.
function f(x, y) {
with (Math) {
return min(round(x), sqrt(y)); // 모호한 참조
}
}
두 가지 경우 모두 with 객체는 프로퍼티를 추출해 내고 블록 내의 지역변수로 바인딩하는데, 정말 쉬워 보인다. 하지만 이런 예제들은 실제로는 기대하는 대로 동작하지 않는다. setBackground, round, sqrt 와 같이 with 객체의 프로퍼티로 참조하기를 기대하는 변수와 외부의 변수 바인딩으로 참조하기를 기대하는 info, x, y같은 변수가 있다. 하지만 실제로는 문법적으로 이 두 종류의 변수들을 구별할 방법이 없다. 그냥 변수처럼 보인다.
사실 자바스크립트는 모든 변수를 동일하게 처리한다. 스코프내에서 변수를 찾을때, 가장 안쪽에서부터 시작해 바깥쪽으로 넓혀가면서 차즌다. with 선언문은 변수 스코프를 대표하는 것처럼 객체를 처리하여, with 블록 내부에서는 주어진 변수 이름을 가진 프로퍼티부터 찾기 시작한다. 객체 내에서 프로퍼티가 발견되지 않는다면, 그때는 외부 스코프로 이어서 탐색한다.
그림 2.1은 with 선언문의 본문이 실행되는 동안 상태 함수 스코프의 자바스크립트 엔진의 내부 표현을 나타낸 도표이다. ES5 명세서에는 어휘적 환경(lexical environment)으로 알려져 있다. (이전 버전에서는 스코프 체인이라고도 불린다.) 이 환경의 가장 안쪽 스코프튼 width 객체로부터 제공된다. 다음 외부 스코프는 함수의 지역변수인 info와 widget의 바인딩을 가진다. 다음 단계에서는 status 함수로의 바인딩을 가진다.
일반적인 스코프에서는 해당 지역 스코프에 있는 변수들의 개수만큼 해당 레벨의 환경이 어떻게 바인딩되는지 주목하라. 하지만 with 스코프에서는 주어진 객체 때에 객체 안에서 무슨일이 일어나는지에 의해서 바인딩의 수가 결정된다.
with에 제공된 객체에 어떤 프로퍼티가 있는지 혹은 없는지를 얼마나 확신할 수 있을까? with 블록 내에서 외부 변수로의 모든 참조는 암묵적으로 with 객체 내에, 혹은 prototype 객체 내에 같은 이름의 프로퍼티가 없다는 것을 가정한다.
이전 예제에서 widget 객체가 info 프로퍼티를 가지게 된다면, status 함수는 갑자기 status 함수의 info 파라미터 대신 widget 객체의 info 프로퍼티를 사용하게 된다. 이러한 상황은 언제든 발생할 수 있다. 또한 런타임시 widget의 prototype 객체에 info 프로퍼티를 추가할 수 있고, 이렇게 되면 status 함수가 예측 불가능한 상황으로 망가지기 시작할 수도 있다.
status("connecting"); // Status: connecting
Widget.prototype.info = "[[widget info]]";
status("connecting"); // Status: [[widget info]]
이와 유사하게, Math 객체에 x나 y 프로퍼티를 추가한다면 예제의 함수 f 또한 망가질수 있다.
Math.x = 0;
Math.y = 0;
f(2, 9); // 0
어떤 특정 객체가 수정될지, 혹은 알지 못하는 프로퍼티를 가지고 있을지 예측하기란 언제나 쉽지 않다. 자바스크립트에는 with의 댕나으로 사용할 만한 직접적인 대체 기능은 없다. 어떤 경우에는 객체를 단순히 짧은 이름의 변수로 바인딩 하는 게 최선의 대안이다.
function status(info) {
var w = new Widget();
w.setBackground("blue");
w.setForeground("white");
w.addText("Status: "+ info);
w.show();
}
이 예제의 동작은 훨씬 더 예측하기 쉽다. 어떠한 변수 참조도 객체 w의 내용에 신경쓰지 않는다. 따라서 어떤 코드가 Widget의 prototype을 수정하더라도 status 함수는 계속해서 기대한 대로 동작할 것이다.
status("connecting"); // Status: connecting
Widget.prototype.info = "[[widget info]]";
status("connected"); // Status: connected
다른 경우에는, 지역 변수를 명시적으로 연관된 프로퍼티와 바인딩 하는 것이 최선의 방법이다.
function f (x, y) {
var min = Math.min, round = Math.round, sqrt = Math.sqrt;
return min(round(x), sqrt(y));
}
with를 제거하면 함수의 동작이 훨씬 더 예측 가능해진다.
- with 선언문을 사용하지 마라.
- 객체로의 반복되는 접근을 위해 짧은 변수 이름을 사용하라
- with 선언문으로 암묵적으로 바인딩하는 대신에 명시적으로 지역 변수를 객체 프로퍼티에 바인딩하라.