[javascript] JavaScript의 “with”문을 합법적으로 사용합니까?

with진술 에 대한 나의 대답에 대한 Alan Storm의 의견 은 저를 생각하게했습니다. 나는이 특정한 언어 기능을 사용해야 할 이유를 거의 찾지 못했고 그것이 어떻게 문제를 일으킬 수 있는지에 대해 많은 생각을하지 않았다. 이제 with함정을 피하면서 어떻게 효과적으로 사용할 수 있는지 궁금 합니다.

with진술 이 어디에 유용 했습니까?



답변

오늘 또 다른 용도가 생겨서 웹을 흥미롭게 검색하고 기존의 언급을 발견했습니다. 블록 범위 내 변수 정의 .

배경

JavaScript는 C 및 C ++의 표면적 유사성에도 불구하고 변수를 정의 된 블록으로 범위를 지정하지 않습니다.

var name = "Joe";
if ( true )
{
   var name = "Jack";
}
// name now contains "Jack"

루프에서 클로저 선언은 오류로 이어질 수있는 일반적인 작업입니다.

for (var i=0; i<3; ++i)
{
   var num = i;
   setTimeout(function() { alert(num); }, 10);
}

for 루프는 새로운 스코프를 도입하지 않기 때문에 num값이 23 인 동일한 함수가 세 함수 모두에 의해 공유됩니다.

새로운 범위 : letwith

의 도입 let에 문 ES6 , 이러한 문제를 방지하기 위해 필요한 경우 새로운 범위를 소개 쉽게된다 :

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

또는:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

ES6을 보편적으로 사용할 수있을 때까지이 사용은 최신 브라우저 및 트랜스 파일러를 사용하려는 개발자로 제한됩니다. 그러나 다음을 사용하여이 동작을 쉽게 시뮬레이션 할 수 있습니다 with.

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

루프는 이제 의도 한대로 작동하여 0에서 2 사이의 값을 갖는 세 개의 개별 변수를 만듭니다 . 블록 에서 선언 된 변수는 C ++의 블록 동작과 달리 범위가 지정되지 않습니다 (C에서 변수는 시작시 선언되어야 함) 블록과 비슷합니다). 이 동작은 실제로 이전 버전의 Mozilla 브라우저에서 소개되었지만 다른 곳에서는 널리 채택되지 않은 let블록 구문 과 매우 유사합니다 .


답변

with 문을 범위가 지정된 가져 오기의 간단한 형태로 사용하고 있습니다. 일종의 마크 업 빌더가 있다고 가정 해 봅시다. 쓰기보다는 :

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

대신 쓸 수 있습니다 :

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

이 사용 사례의 경우 할당을 수행하지 않으므로 이와 관련된 모호성 문제가 없습니다.


답변

이전의 의견에서 알 수 있듯이 with주어진 상황에서 아무리 유혹을 느끼더라도 안전하게 사용할 수 있다고 생각하지 않습니다 . 이 문제는 여기서 직접 다루지 않으므로 반복하겠습니다. 다음 코드를 고려하십시오

user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);

with(user){
    name = 'Bob';
    age  = 20;
}

이러한 함수 호출을주의 깊게 조사하지 않으면이 코드가 실행 된 후 프로그램의 상태를 알 수있는 방법이 없습니다. user.name이미 설정되어 있으면 이제됩니다 Bob. 이 설정되지 않은 경우, 세계는 name초기화 또는 변경됩니다 Bobuser개체가없이 유지됩니다 name속성입니다.

버그가 발생합니다. 함께 사용 하면 하면 결국이 작업을 수행하고 프로그램이 실패 할 가능성을 높입니다. 더구나, 의도적으로 또는 작성자를 통해 with 블록에 전역을 설정하는 작업 코드가 발생할 수 있습니다. 그것은 스위치에서 쓰러지는 것과 매우 흡사합니다. 저자가 이것을 의도했는지 전혀 알지 못하며 코드를 “고정”하여 회귀를 일으킬 지 알 수 없습니다.

최신 프로그래밍 언어에는 기능이 가득합니다. 몇 년 동안 사용한 후 일부 기능은 불량한 것으로 밝혀져 피해야합니다. 자바 스크립트 with도 그중 하나입니다.


답변

나는 실제로 그 with진술이 최근에 매우 유용하다는 것을 알았습니다 . 이 기술은 현재 프로젝트 (JavaScript로 작성된 명령 행 콘솔)를 시작할 때까지 실제로 발생하지 않았습니다. 특수 명령을 콘솔에 입력 할 수 있지만 전역 범위의 변수를 재정의하지 않는 Firebug / WebKit 콘솔 API를 에뮬레이션하려고했습니다. 나는 Shog9의 훌륭한 답변 에 대한 의견에서 언급 한 문제를 극복하려고 할 때 이것을 생각했습니다 .

이 효과를 달성하기 위해 전역 범위 뒤에있는 범위를 “계층화”하기 위해 두 개의 with 문을 사용했습니다.

with (consoleCommands) {
    with (window) {
        eval(expression);
    }
}

이 기술의 가장 큰 장점은 성능상의 단점을 제외하고는 with우리가 어쨌든 세계적인 범위에서 평가하고 있기 때문에 성명서에 대한 일반적인 두려움을 겪지 않는다는 것입니다. 수정되었습니다.

놀랍게도 다른 곳에서 사용 된 것과 동일한 기술인 Chromium 소스 코드 를 찾았을 때이 답변을 게시 할 수있었습니다 .

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

편집 : 방금 Firebug 소스를 확인했으며 더 많은 레이어에 대해 4 개의 문을 묶었 습니다. 미친!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";


답변

예, 그렇습니다 매우 합법적 인 사용이 있습니다. 손목 시계:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

기본적으로 다른 DOM 또는 CSS 후크는 환상적인 사용법입니다. “CloneNode”는 정의되지 않은 상태가 아니므로 사용자가 방해가되지 않고 가능한 한 글로벌 범위로 돌아갑니다.

Crockford의 속도 불만은 with에 의해 새로운 맥락이 만들어 졌다는 것입니다. 문맥은 일반적으로 비싸다. 동의한다. 그러나 방금 div를 만들었고 CSS를 설정하기위한 프레임 워크가없고 15 가지 정도의 CSS 속성을 직접 설정 해야하는 경우 컨텍스트를 만드는 것이 변수 생성 및 15 역 참조보다 저렴합니다.

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

기타…


답변

with모호함없이 이점을 제공하기 위해 작은 도우미 함수를 정의 할 수 있습니다 .

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});


답변

다음을 수행 할 수 있으므로 그만한 가치가없는 것 같습니다.

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";