with
지원이 중단되었습니다: 이 기능은 더 이상 권장되지 않습니다. 일부 브라우저에서는 아직 지원할 수 있지만 관련 웹 표준에서 이미 제거되었거나 제거 과정에 있는 경우가 있으며, 호환성을 위해 유지되고 있을 수 있습니다. 사용을 피하고 가능하다면 기존 코드를 업데이트하세요. 결정을 지원할 하단의 호환성 표를 확인하세요. 이 기능은 언제든지 작동을 중단할 수 있음을 유의하세요.
참고:
with
문의 사용은 권장되지 않습니다. 혼란스러운 버그와 호환성 문제의 원인이 될 수 있고 최적화를 불가능하게 만들며 엄격 모드에서 금지되어 있기 때문입니다. 속성에 접근하고자 하는 객체를 임시 변수에 할당하는 것이 대안으로 권장됩니다.
with
문은 문의 스코프 체인을 확장합니다.
구문
with (expression)
statement
expression
-
문을 평가할 때 사용되는 스코프 체인에 주어진 표현식을 추가합니다. 표현식 주위의 괄호는 필수입니다.
statement
-
임의의 문. 여러 개의 문을 실행하려면 블록 문(
{ ... }
)을 사용하여 문들을 묶습니다.
설명
식별자에는 두 가지 유형이 있습니다. '제한된' 식별자와 '제한되지 않은' 식별자입니다. 제한되지 않은 식별자는 해당 식별자가 어디서 왔는지 나타내지 않는 식별자입니다.
foo; // 제한되지 않은 식별자
foo.bar; // bar는 제한된 식별자
일반적으로 제한되지 않은 식별자는 그 이름을 가진 변수를 찾기 위해 스코프 체인을 검색함으로써 이행됩니다. 반면에 제한된 식별자는 그 이름을 가진 속성을 찾기 위해 객체의 프로토타입 체인을 검색하여 이행됩니다.
const foo = { bar: 1 };
console.log(foo.bar);
// foo는 스코프 체인에서 변수로 발견된다.
// bar는 foo에서 속성으로 발견된다.
이 규칙의 예외는 전역 객체로, 이는 스코프 체인의 최상위에 위치하여 전역 객체의 속성들은 자동적으로 제한자 없이 참조할 수 있는 전역 변수가 됩니다.
console.log(globalThis.Math === Math); // true
with
문은 문의 본문이 평가되는 동안 주어진 객체를 스코프 체인의 맨 앞에 추가합니다. 모든 제한되지 않은 이름은 상위 스코프 체인을 검색하기 전에 먼저 주어진 객체 내에서 (in으로 확인하여) 검색됩니다.
참고로 제한되지 않은 참조가 객체의 메서드를 조회하는 경우 그 메서드는 with
문에 주어진 객체를 this
값으로 하여 호출됩니다.
with ([1, 2, 3]) {
console.log(toString()); // 1,2,3
}
객체는 @@unscopables
속성을 가질 수 있으며, 이 속성은 (하위 호환성을 위해) 스코프 체인에 추가되지 않아야 하는 속성의 목록을 정의합니다. 자세한 내용은 Symbol.unscopables 문서를 참고하십시오.
with
문을 사용하는 이유는 임시 변수를 하나 줄이는 것과 긴 객체 참조를 반복하는 걸 피함으로써 파일 크기를 줄이는 것입니다. 그러나 with
문이 바람직하지 않은 이유들은 훨씬 더 많고 다음과 같습니다.
-
성능:
with
문은 모든 이름 조회시에 지정된 객체를 첫번째로 검색하게 합니다. 따라서 지정된 객체의 속성이 아닌 모든 식별자는with
블록 내에서 더 느리게 검색됩니다. 게다가 최적화 도구가 제한되지 않은 식별자 각각이 무엇을 참조하는지에 대해 어떤 가정도 할 수 없으므로, 식별자가 사용될 때마다 동일한 속성 조회를 반복해야 합니다. -
가독성:
with
문은 인간 독자나 JavaScript 컴파일러가 제한되지 않은 이름이 스코프 체인에서 발견될지, 만약 그렇다면 어떤 객체에서 발견될지를 판단하기 어렵게 만듭니다. 다음은 그 예시입니다.jsfunction f(x, o) { with (o) { console.log(x); } }
f
의 정의만 보면with
본문 내의x
가 무엇을 참조하는지 알 수 없습니다.x
가o.x
인지f
의 첫 번째 형식 매개변수인지는f
가 호출될 때만 알 수 있습니다. 두 번째 매개변수로 전달하는 객체에x
를 정의하는 것을 잊어도 오류가 발생하지 않습니다. 대신 예상치 못한 결과가 나올 뿐입니다. 또한 이러한 코드는 실제 의도가 무엇인지도 불분명합니다. -
향후 호환성:
with
를 사용하는 코드는 특히 단순한 객체가 아닌 대상과 함께 사용될 때 향후 호환되지 않는 코드가 될 수 있습니다. 이는 해당 대상이 미래에 더 많은 속성을 갖게 될 수 있기 때문입니다. 다음 예시를 살펴보겠습니다.jsfunction f(foo, values) { with (foo) { console.log(values); } }
ECMAScript 5 환경에서
f([1, 2, 3], obj)
를 호출하면,with
문 내의values
참조는obj
로 이행됩니다. 그러나 ECMAScript 2015는Array.prototype
에values
속성을 도입합니다(따라서 모든 배열에서values
속성을 사용할 수 있게 됩니다). 따라서 환경을 업그레이드하면with
문 내의values
참조는[1, 2, 3].values
로 이행되며, 이는 버그를 일으킬 가능성이 큽니다.이 특정 예제에서는
values
가Array.prototype[@@unscopables]
에 의해 스코프 지정 불가능하게 정의되어 있어 해당 코드의values
는 여전히values
매개변수로 올바르게 이행됩니다. 만약values
가 스코프 지정 불가능으로 정의되지 않았다면, 이는 디버깅하기 어려운 문제가 될 수 있습니다.
예제
with문 사용하기
현재 스코프에서 속성 구조 분해 할당을 사용하여 with문 피하기
대부분의 경우 속성 구조 분해 할당을 통해서 with
사용을 피할 수 있습니다. 여기서는 with
가 추가 스코프를 생성하는 동작을 모방하기 위해 추가 블록을 만들었지만 실제 사용에서는 이 블록을 보통 생략할 수 있습니다.
let a, x, y;
const r = 10;
{
const { PI, cos, sin } = Math;
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
IIFE를 사용하여 with문 피하기
만약 긴 이름의 참조를 여러 번 재사용해야 하는 표현식을 만들고 있으며 그 긴 이름을 표현식 내에서 제거하려고 한다면 IIFE로 표현식을 감싸고 긴 이름을 인수로 제공할 수 있습니다.
const objectHavingAnEspeciallyLengthyName = { foo: true, bar: false };
if (((o) => o.foo && !o.bar)(objectHavingAnEspeciallyLengthyName)) {
// 이 분기가 실행됩니다.
}
with문과 프록시를 사용해서 동적 네임스페이스 생성하기
with
는 모든 변수 조회를 속성 조회로 변환하는 반면 프록시는 모든 속성 조회 호출을 가로챌 수 있게 합니다. 이를 결합하여 동적 네임스페이스를 생성할 수 있습니다.
const namespace = new Proxy(
{},
{
has(target, key) {
// `console`과 같은 전역 속성을 가로채는 걸 피하십시오.
if (key in globalThis) {
return false;
}
// 모든 속성 조회를 가로챕니다.
return true;
},
get(target, key) {
return key;
},
},
);
with (namespace) {
console.log(a, b, c); // "a" "b" "c"
}
명세서
Specification |
---|
ECMAScript Language Specification # sec-with-statement |
브라우저 호환성
BCD tables only load in the browser