JavaScript의 "with"문을 합법적으로 사용합니까?


369

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

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


52
나는 그것을 사용하지 않습니다. 내가 존재하지 않는 척하면 그것없이 사는 것이 더 편합니다.
Nosredna 2016 년

6
한 번에 많은 유효한 용도가 있었을 것입니다. 하지만 헛소리입니다. ES5 Strict가 제거 with되어 더 이상 그런 것이 없습니다.
Thomas Aylott

27
ES5 Strict는 여전히 선택 사항 입니다.
Shog9

5
ES5 strict에서 'with'를 제거하는 대신 변수를 찾지 못하면 'with'내부에 지정된 할당이 인수 객체에 바인딩되도록 표준을 변경하는 것이 낫지 않았습니까?
JussiR

2
@JussiR : 아마. 그러나 그렇게하는 데있어 문제는 오래된 브라우저에서 문제가 발생할 수 있다는 것입니다.
Sune Rasmussen

답변:


520

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

배경

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블록 구문 과 매우 유사합니다 .


15
리터럴과 함께 사용하는 것을 생각하지 마십시오. 합법적 인 것 같습니다.
매트 칸토르

81
이건 정말 죽었어 나는 이런 식으로 JavaScript의 범위를 가지고 노는 것을 생각한 적이 없다. 코딩으로 새로운 영역을 완전히 확장했습니다. 내가 10 번 공표 할 수 있으면 좋겠다!
kizzx2

27
여전히 반대하는 사람들에게는 항상 폐쇄를 사용할 수 있습니다.for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
실제로 위에 링크 된 문제는 대부분의 Mozilla 브라우저가 아닌 브라우저 (Chrome, Safari, Opera, IE)에 나타납니다.
Max Shawabkeh

24
하자 IE에서 문 지원은 지금 당장 내 베이컨을 절약 할, 내가 사용 여부에 나의 양심과 사투를 벌인거야 으로 대신. 실제 문제는 withlet 을 사용하더라도 프로토 타입 체인에서 객체의 상속 된 속성으로 인해 여전히주의를 기울여야한다는 것입니다. 예를 들면 다음과 같습니다 var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. with 문의 범위 에서 toString ()Object 의 상속 된 속성 이므로 명시 적으로 정의 된 함수는 호출되지 않습니다. 그래도 여전히 좋은 대답입니다 :-)
Andy E

161

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')
    )
  )
}

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


5
그것이 VB에서 사용 된 것을 보았습니다. (그리고 내가 아는 유일한 용도)
Mateen Ulhaq

2
이것의 단점은 markupbuilder 객체 외부에있는 with 블록 내에서 변수를 참조하면 js 엔진은 먼저 markupbuilder 내에서 변수를 검색하여 성능을 저하 시킨다는 것입니다.
Adam Thomas

3
이것은 캔버스 경로로 작업하는 사람들을 위해 코드를 줄이는 데 실제로 도움이됩니다.
Brian McCutchon

4
해당 코드의 "with"버전은 문자 그대로 "with"버전보다 240 배 이상 느립니다. 그것이 사람들이 합법적으로 사용하지 않는다고 말하는 이유입니다. 코드를 더 예쁘게 만들 수 없기 때문이 아닙니다. 벤치 마크를 참조하십시오 jsfiddle.net/sc46eeyn
짐보 조니

1
@McBrainy-그것은 훨씬 느리게 실행되는 코드를 사용해서는 안되는 장소 유형입니다 (방금 작성한 주석 참조). 매우 반복되는 코드에 대한 단축키가 필요한 경우이를 선언 할 수 있습니다. 예를 들어 context.bezierCurveTo백 번 똑바로 사용 하는 경우 전화를 걸 때마다 말하고 var bc2 = context.bezierCurveTo;bc2(x,x,etc);수 있습니다. 그것은 매우 빠르며 덜 장황한 반면, with매우 느립니다.
Jimbo Jonny

83

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

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

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

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

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

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


18
이 문제는 객체의 속성에 값을 할당 할 때만 나타납니다. 그러나 값을 읽기 위해서만 사용한다면 어떨까요? 나는 그 경우에 그것을 사용하는 것이 좋다고 주장한다.
airportyh

10
Toby 값을 읽을 때도 같은 문제가 발생합니다. 위의 코드 스 니펫에서 이름이 사용자 개체에 설정되어 있는지 알 수 없으므로 전역 이름 또는 사용자 이름을 읽고 있는지 알 수 없습니다.
Alan Storm

12
값을 읽으면 명확한 우선 순위 규칙이 있습니다. 객체의 속성은 범위 밖의 변수보다 먼저 확인됩니다. 이것은 함수에서 범위를 정하는 변수와 다르지 않습니다. 할당과 'with'의 실제 문제는 내가 이해하는 것처럼 속성 할당이 발생하는지 여부는 해당 속성이 문제의 현재 객체에 존재하는지 여부에 달려 있다는 사실에 있습니다.이 속성은 런타임 속성이며 쉽게 추론 할 수 없습니다 코드를 보면
airportyh

1
나는 당신이 거기 토비있을 수 있다고 생각합니다. 쓰기 문제는 구조체에서 완전히 부끄러워하기에 충분합니다.
Alan Storm

"스위치에서 쓰러지는 것과 많이 비슷합니다. 당신은 모른다 ..." -그럼 switch ()도 금지하자? ;-p
Sz.

66

나는 실제로 그 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);" +
    "}" +
"}}}}";

1
ecmascript5가이 작업을 수행하지 못하게 할까 걱정합니다. ecmascript 5 솔루션이 있습니까?
kybernetikos

@Adam : 확실하지 않습니다. ES5는 엄격 모드에서이 오류 만 발생하므로 엄격하게 전역 모드를 선언하지 않은 경우에는 즉시 문제가되지 않습니다. ES Harmony는 더 큰 문제를 야기 할 수 있지만 프록시와 같은 일부 새로운 기능으로 해결할 수 있습니다.
Andy E

@AndyE이 주제와 관련하여 죄송하지만 'JavaScript로 작성된 명령 줄 콘솔'은 어디에서나 사용할 수 있습니까?
kybernetikos

@ 아담 : 아니, 그렇지 않다. 모든 것은 Windows 데스크톱 가제트를위한 일련의 개발자 도구로 만들어졌지만 완료하지는 않았습니다 (콘솔은 매우 잘 작동하지만). 현재 WDG의 미래는 밝지 않지만 어느 시점에서 마무리 할 수 ​​있습니다.
Andy E

3
몇 주 전에 블록에서 ES6 기능을 차단했기 때문에 Chrome의 콘솔 구현을 블록에서 기호로 전환했습니다. :)
Alexey Kozyatinskiy

54

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

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";

기타...


5
+1도 합법적으로 많이 사용한다고 생각합니다 with. 그러나이 특별한 경우에는 다음과 같이 할 수 있습니다.element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
extendjQuery 또는 Underscore.js 의 간단한 메소드를 사용하여 한 줄로 동일한 작업을 수행 할 수 $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'})있습니다.
Trevor Burnham 2016 년

9
@TrevorBurnham-jQuery를 사용할 수 있다고 가정한다면, 그 .css()방법을 사용하면 된다.
nnnnnn

4
이 변수가 전역 범위로 들어가는 것을 정확히 방해하는 것은 무엇입니까? 모든 CSS 스타일이 항상 모든 요소에 정의되어 있기 때문입니까?
mpen December

1
@Mark 예, 속성에 대한 사용자 정의 스타일이없는 경우 항상 null 또는 빈 문자열을 값으로 사용하여 정의됩니다.
Esailija

34

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

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

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

8
세상에, 내 머리가 폭발했다! 모호함 없이 ? 투표해라!
Jarrod Dixon

@Jarrod : 이것에 대해 너무 재밌는 것은 무엇 입니까 ( is.gd/ktoZ )? 이 사이트를 사용하는 대부분의 사람들은 나보다 똑똑하기 때문에 내가 틀렸다면 용서하십시오.
raven

14
그러나 그것은 이해하는 방법을 이해하는 데 더 길고 어렵습니다. var _ = obj_name_here; _.a = "foo"; _.b = "bar;
르네 자르 수

3
Rene : 이로 인해 "_"변수가 외부 범위에 노출되어 잠재적 인 버그가 발생합니다. 또한 객체 매개 변수를 계산하는 데 사용 된 임시 변수를 노출합니다.
John Millikin

30
with_더러워진 두 배 버전 (function(_){ _.a="foo"; })(object_here);(c / java 스타일 블록을 시뮬레이션하는 표준 방법)으로 인해 더 많은 버그 가 발생합니다 . 대신 사용하십시오.
mk.

25

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

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

1
이것은 나에게 의미가 없었습니다. 나는 자바 스크립트에서 전문가가 아니에요 있지만
JeroenEijkhof

@WmasterJ 명확성을 위해이 게시물을 참조하십시오 : yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis

8
긴 변수 이름이 유일한 사용 사례는 아닙니다 with.
Chris

18

나는 함께 사용하지 않으며, 이유를 보지 않으며, 권장하지 않습니다.

문제 는 ECMAScript 구현이 수행 할 수있는 수많은 어휘 최적화를 방지with 한다는 것 입니다. 빠른 JIT 기반 엔진의 등장으로이 문제는 가까운 시일 내에 더욱 중요해질 것입니다.

것처럼 보일 수 있습니다 with(말하자면, 대신 공통 익명 함수 래퍼의 새로운 범위를 도입하거나 앨리어싱 자세한 교체 할 때), 그러나 그것의 청소기 구조를 허용 정말 그럴 가치가 없어 . 성능 저하 외에도 항상 잘못된 개체의 속성 (사출 된 범위의 개체에서 속성을 찾을 수없는 경우)에 할당하고 전역 변수를 잘못 도입 할 위험이 있습니다. IIRC, 후자의 문제는 Crockford가 피하도록 권장 한 것 with입니다.


6
bogeyman의 성능은 거의 전 세계적으로 자주 뛸 수 있습니다. 우리가 이야기하고있는 JavaScript 라는 점을 감안할 때 항상 저를 괴롭 힙니다 . 많은 관심을 끌기 위해 성능 히트가 실제로 극적 이라고 가정 할 것입니다 ...하지만 with(){}다른 답변, 현대 브라우저에서 제공되는 것과 같은 구조 비용에 어려운 숫자가있는 경우 보고 싶습니다. 그들!
Shog9

6
Javascript와 관련하여 왜 이상한가요? :) 그리고 그렇습니다. 생각해보십시오-구현은 괄호로 표현을 평가하고, 그것을 객체로 변환하고, 현재 스코프 체인의 앞에 삽입하고, 블록 내부의 명령문을 평가 한 다음, 스코프 체인을 다시 정상으로 복원해야합니다. 즉 A의 많은 작품. 고도로 최적화 된 저수준 코드로 전환 될 수있는 단순한 속성 조회 이상의 것입니다. 차이점을 보여주는 방금 만든 매우 간단한 벤치 마크 (실수를 발견하면 알려주세요) -gist.github.com/c36ea485926806020024
kangax

5
@ kangax : 나는 C ++ 배경에서 왔는데, 많은 프로그래머가 실제로 큰 루틴이나 프로그램의 성능에 눈에 띄는 영향을 미치지 않더라도 코드의 작은 효율성에 집착하는 것이 일종의 전통입니다. 일상적인 성능의 많은 부분이 VM 구현에 따라 달라질 수있는 JavaScript와 관련하여 저에게는 이상해 보입니다. JS 프로그래머가 설정 비용에 대한 우려로 인해 익명의 기능을 피할 수있는 몇 가지 사례를 보았지만 매우 민감한 코드 영역을 위해 예약 된 규칙이 아닌 예외 인 것 같습니다.
Shog9

5
즉, 당신은 비용과 관련하여 절대적으로 정확합니다 with(){}: 새로운 범위를 설정하는 것은 with테스트 한 모든 브라우저에서 매우 비쌉니다. 매우 자주 호출되는 코드에서는 이것을 피하고 싶을 것입니다. 또한 Chrome은 with()범위 내에서 실행되는 모든 코드에 큰 타격을 입었습니다 . 흥미롭게도 IE는 with()블록 내 코드에 대해 최고의 성능 특성을 가졌 습니다. 설정 비용을 고려하지 않고 with()IE6 및 IE8 VM에서 가장 빠른 구성원 액세스 수단을 제공합니다 (이 VM은 전체적으로 가장 느리지 만). 좋은 물건, 감사합니다 ...
Shog9

5
FWIW : 설정 비용을 고려한 동일한 테스트 세트는 다음과 같습니다. jsbin.com/imidu/edit Chrome의 변수 액세스 with()는 Chrome에서 거의 10 배 느리고 IE에서는 두 배 이상 빠릅니다 ...!
Shog9

13

Visual Basic.NET도 비슷한 With문장을 가지고 있습니다. 내가 사용하는 가장 일반적인 방법 중 하나는 여러 속성을 빠르게 설정하는 것입니다. 대신에:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

, 난 쓸수있다:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

이것은 단지 게으름의 문제가 아닙니다. 또한 훨씬 더 읽기 쉬운 코드를 만듭니다. 그리고 JavaScript와 달리 명령문의 영향을받는 모든 부분 앞에 .(점) 을 붙여야하기 때문에 모호성이 없습니다 . 따라서 다음 두 가지는 명확하게 구분됩니다.

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

전자는 someObject.Foo; 후자는 외부Foo 범위에 있습니다 someObject .

JavaScript의 구분이 없기 때문에 모호성의 위험이 너무 높기 때문에 Visual Basic의 변형보다 훨씬 유용하지 않습니다. 그 외에는 with여전히 가독성을 향상시킬 수있는 강력한 아이디어입니다.


2
충분합니다. 그래도 그의 질문에 대답하지 않습니다. 그래서 주제에서 벗어났습니다.
Allain Lalonde

6
이것은 내 마음을 통해 실행되었습니다. 누군가는 그것을 말해야했다 . 왜 JavaScript가 점을 가질 수 없습니까?
카슨 마이어스

동의했다, 점 표기법이 우수하다, JavaScript가 그것을 사용하기를 바란다. +1


7

"with"를 사용하면 코드를 더 건조하게 만들 수 있습니다.

다음 코드를 고려하십시오.

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

다음과 같이 건조시킬 수 있습니다.

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

나는 당신이 가독성이나 표현력을 선호하는지 여부에 달려 있다고 생각합니다.

첫 번째 예제는 더 읽기 쉽고 대부분의 코드에 권장됩니다. 그러나 대부분의 코드는 어쨌든 꽤 길들입니다. 두 번째는 조금 더 모호하지만 코드의 크기와 불필요한 변수를 줄이기 위해 언어의 표현적인 특성을 사용합니다.

Java 또는 C #을 좋아하는 사람들이 첫 번째 방법 (object.member)을 선택하고 Ruby 또는 Python을 선호하는 사람들이 후자를 선택한다고 생각합니다.


나는 누군가 1 년 전에 기본적 으로이 동일한 예제를 이미 게시 한 것을 알지 못했습니다. "with"와 같은 성능 문제는 조금 더 읽기 어려우면서 멋진 DRY 코드를 만듭니다. 다른 개발자 또는 대부분의 프로덕션 코드와의 공동 작업에는 "with"키워드를 사용하지 않는 것이 좋습니다. 그러나 전문가 수준의 프로그래머와 함께 일하고 잠재적 비 효율성을 피하는 방법을 이해한다면 반드시 "with"를 사용하여 마을로 가십시오.
요나

6

나는 명백한 사용이 지름길이라고 생각합니다. 예를 들어 객체를 초기화하는 경우 많은 "ObjectName"을 입력하면됩니다. lisp의 "with-slots"처럼 쓸 수있는 종류

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

글쓰기와 같습니다

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

언어가 "Objectname.foo"를 허용하지만 여전히 그래도 이것이 왜 지름길인지 더 분명합니다.


1
lisp 코드를 보는 것이 좋습니다! 자바 스크립트에서 "with"는 언어의 체계적인 뿌리에서 영감을 얻은 것으로 생각되지만 자바 스크립트 질문에 LISP를 게시 한 것에 대한 공감대가 아프다.
불 까마귀

1
기본 원칙에 대한지지. 'with'는 놀랍도록 강력한 구성입니다. 그러나 대부분의 JS 사용자는 클로저를 이해하지 못하고 JS 위에 엄청나게 복잡한 Java 클래스 상속 시스템을 작성하므로 어떻게 'with'가 제공하는 메타 프로그래밍 가능성을 알 수 있을까요?
jared

물론 with-slots사용중인 슬롯을 지정해야하지만 with런타임에 바인딩되는 슬롯을 사용합니다.
사무엘 에드윈 워드 1

6

델파이와 경험을 갖는 것은, 그 사용 말할 것 과를 것이 최후의 리 소드 크기 최적화이어야하며 안전성을 확인하기 위해 정적 코드 분석에 액세스 할 수있는 일종의 자바 스크립트 최소화 알고리즘에 의해 수행 될 수 있습니다.

with 문 을 자유로이 사용하여 얻을 수있는 범위 지정 문제 는 a **에 큰 고통이 될 수 있으며 누구도 디버깅 세션을 경험하여 코드에서 무슨 일이 일어나고 있는지 파악하고 싶지는 않습니다. 전역 또는 외부 범위 변수 대신 객체 멤버 또는 잘못된 로컬 변수를 캡처 한 경우에만 찾을 수 있습니다.

VB with statement는 범위를 명확하게하기 위해 점이 필요하다는 점에서 더 좋습니다. 그러나 Delphi with statement는 헤어 트리거가있는로드 된 총이며, 자바 스크립트 하나가 동일한 경고를 보장 할만 큼 충분히 비슷한 것처럼 보입니다.


5
문이있는 자바 스크립트는 델파이보다 나쁩니다. Delphi에서 with는 object.member 표기법보다 빨리 (빠르지 않은 경우) 수행합니다. 자바 스크립트에서 with는 일치하는 멤버를 확인하기 위해 범위를 걸어야하므로 항상 object.member 표기법보다 느립니다.
Martijn

5

with를 사용하는 것은 권장되지 않으며 ECMAScript 5 엄격 모드에서는 금지되어 있습니다. 권장되는 대안은 속성을 액세스하려는 객체를 임시 변수에 할당하는 것입니다.

출처 : Mozilla.org


4

with 문을 사용하여 코드 크기를 줄이거 나 개인 클래스 멤버에 사용할 수 있습니다 (예 :

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

with 문은 범위를 수정하려는 경우 런타임에 조작 할 수있는 고유 한 글로벌 범위를 갖는 데 필요한 것입니다. "toUpper", "toLower"또는 "isNumber", "clipNumber"aso와 같이 자주 사용되는 특정 도우미 함수에 상수를 넣을 수 있습니다.

나쁜 성능에 대해 자주 읽습니다. 함수를 범위 지정해도 성능에 영향을 미치지 않습니다. 실제로 FF에서는 범위가 지정된 함수가 범위가없는 것보다 빠르게 실행됩니다.

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

따라서 위에서 언급 한 방식으로 with 문은 성능에 부정적인 영향을 미치지 않지만 코드 크기를 줄이면 모바일 장치의 메모리 사용에 영향을 미치는 좋은 방법입니다.


3

with를 사용하면 많은 구현에서 코드가 더 느려집니다. 이제 모든 것이 조회를 위해 추가 범위로 ​​래핑되기 때문입니다. JavaScript에서 with를 사용할 정당한 이유는 없습니다.


5
조기 최적화. 당신이 숫자를 아프게하지 않는 한 "느리게"주장하지 마십시오. 현대 및 고대 js impls에서 모든 오버 헤드가 사소한 것 같습니다.
mk.

2
귀하 이외의 개발자에게는 문제가되지 않을 수있는 내용에 근거하여 귀하의 결론에 크게 동의하지 않습니다.
Dave Van den Eynde

4
@ mk : 좋아, 숫자 크 런칭은 여기 var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;에 평균 2500을 var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;제공하고 평균 750 을 제공하여 3 배 이상 느린 속도를 사용합니다.
yorick

3
이것을 보았을 때 콘솔의 Chrome 23에서 실행했습니다. 내가 얻은 결과는 with코드에 대해 1138 이고 그렇지 않은 경우 903이었습니다. 타이트한 루프에서도 이러한 작은 차이로 성능에 대해 걱정하기 전에 코딩 단순성과 사례별로 리팩토링의 용이성을 기반으로 선택합니다.
Plynx

3

템플릿 언어를 JavaScript로 변환 할 때 with 문이 유용 할 수 있다고 생각합니다. 예를 들어 JST 에서 base2 ,하지만 난 더 자주 봤어요.

나는 진술없이 이것을 프로그래밍 할 수 있다는 데 동의합니다. 그러나 문제를 일으키지 않기 때문에 합법적으로 사용됩니다.


3

비교적 복잡한 환경에서 실행되는 코드를 컨테이너에 넣는 것이 좋습니다. "창"에 대한 로컬 바인딩을 만들고 웹 브라우저를위한 코드를 실행하는 데 사용합니다.


3

클로저를 사용하는 드롭 인 대체와 같이 객체 리터럴 사용이 흥미 롭습니다.

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

또는 with 문과 유사한 문

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

실제 위험은 with 문의 일부가 아닌 변수를 실수로 최소화하는 것이라고 생각합니다. 그래서 객체 리터럴이 전달되는 것을 좋아하는 이유는 코드의 추가 컨텍스트에서 무엇이 될지 정확하게 볼 수 있습니다.


3

나는 with진술 과 함께이 모호성을 제거하는 "병합"기능을 만들었다 :

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

와 비슷하게 사용할 수 with있지만 영향을 미치지 않는 범위에는 영향을 미치지 않습니다.

용법:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}

3

몇 가지 간단한 코드 조각을 위해, 나는 같은 삼각 함수 사용하고자하는 sin, cos정도 모드에서 대신 복사 모드 등. 이를 위해 AngularDegree객체를 사용 합니다.

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

그런 다음 with블록 에서 추가 언어 노이즈없이 삼각 모드에서 삼각 함수를 사용할 수 있습니다 .

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

즉, 객체를 함수 모음으로 사용하여 제한된 코드 영역에서 직접 액세스 할 수 있습니다. 나는 이것이 유용하다고 생각한다.


with이 방법으로 문장 을 사용하는 것은 좋은 생각 이 아닙니다. 어떤 함수가 전역이고 어떤 함수가 객체 범위에서 호출되는지 알 수 없기 때문에 코드를 읽기가 어렵습니다. 객체 범위는 전역 네임 스페이스에서 액세스하려고 시도합니다
Saket Patel

범위 지정 문제를 알고 있지만 여전히 유용합니다. 코드를 읽는 수학자는 위의 공식이 구형 삼각법의 "4 개의 연속 부분의 법칙"의 적용이라는 것을 직접보고 싶어합니다. 엄격한 대안은 공식을 난독 화합니다. z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;동일한 결과를 줄 것이지만 그것은 공포입니다.
rplantiko

@rplantiko 명심해야 할 것은 대부분의 사람들이 그것에 익숙하지 않다는 것입니다. 따라서 코드를 작성하지 않으면 누구도 만지지 않을 것입니다. 또한, 나는 당신에게 그 두 가지 사용법을 보여줄 수 with있다고 확신합니다.
Juan Mendes 2016 년

2

유용성이 with코드 작성 방법에 달려 있다고 생각합니다 . 예를 들어 다음과 같은 코드를 작성하는 경우 :

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

그런 다음 with이를 수행하여 코드의 가독성을 향상시킬 것이라고 주장 할 수 있습니다 .

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

반대로, 당신이 데메테르 법칙을 위반하고 있다고 주장 할 수 있지만, 다시는 그렇지 않을 수도 있습니다. 나는 digress =).

무엇보다도 Douglas Crockford는 사용 하지 않는 것이 좋습니다 with. 난 당신에 대한 자신의 블로그 게시물을 체크 아웃 할 것을 촉구 with하고 대안을 여기에 .


답변 주셔서 감사합니다, 톰 나는 Crockford의 권장 사항을 읽었으며 이해하는 것은 지금까지만 진행됩니다. 나는 doekman에 의해 간접적으로 만져진 아이디어에 대해 생각하고있다. with {}의 진정한 힘은 스코프를 조작하는 데 사용될 수있는 방법이다 ...
Shog9

2

나는 with를 사용하는 것이 object.member를 입력하는 것보다 더 읽기 쉬운 방법을 실제로 보지 못합니다. 나는 그것이 덜 읽기 쉽다고 생각하지 않지만 더 읽기 쉽다고 생각하지 않습니다.

lassevk가 말했듯이 with를 사용하는 것이 매우 명백한 "object.member"구문을 사용하는 것보다 오류가 발생하기 쉬운 방법을 확실히 알 수 있습니다.


1

W3schools http://www.w3schools.com/js/js_form_validation.asp 에서 자바 스크립트로 양식의 유효성을 확인해야합니다 . 여기서 "양식"이라는 이름의 입력을 찾기 위해 오브젝트 양식이 "스캔"됩니다.

그러나 양식의 필드 이름이나 수량에 관계없이 모든 필드가 비어 있지 않은 것으로 확인되도록 모든 형식에서 가져 오도록 수정했습니다. 글쎄, 나는 텍스트 필드 만 테스트했습니다.

그러나 with ()는 일을 더 단순하게 만들었습니다. 코드는 다음과 같습니다.

function validate_required(field)
{
with (field)
  {
  if (value==null||value=="")
    {
    alert('All fields are mandtory');return false;
    }
  else
    {
    return true;
    }
  }
}

function validate_form(thisform)
{
with (thisform)
  {
    for(fiie in elements){
        if (validate_required(elements[fiie])==false){
            elements[fiie].focus();
            elements[fiie].style.border='1px solid red';
            return false;
        } else {elements[fiie].style.border='1px solid #7F9DB9';}
    }

  }
  return false;
}

1

CoffeeScript의 Coco 포크에는 with키워드가 있지만 블록 내의 대상 객체 this로 간단히 ( @CoffeeScript / Coco에서 쓰기 가능) 설정합니다 . 이는 모호성을 제거하고 ES5 엄격한 모드 준수를 달성합니다.

with long.object.reference
  @a = 'foo'
  bar = @b

0

다음 with은 객체에 저장된 값을 기반으로 객체 리터럴에 새 요소를 추가 하는 데 유용합니다 . 오늘 방금 사용한 예는 다음과 같습니다.

사용할 수있는 가능한 타일 세트 (위쪽, 아래쪽, 왼쪽 또는 오른쪽을 향한 개구부)가 있었고 게임 시작시 항상 배치되고 잠길 타일 목록을 추가하는 빠른 방법을 원했습니다. . types.tbr목록의 각 유형에 대해 입력을 계속하고 싶지 않으므로 방금 사용했습니다 with.

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");

0

require.js를 사용할 때 명시 적으로 arity를 ​​관리하지 않아도하기 위해 with를 사용할 수 있습니다.

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

requirejs.declare의 구현 :

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },

        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}

0

Andy E가 Shog9의 답변에 대한 의견에서 지적 했듯이이 잠재적으로 예기치 않은 동작 with은 객체 리터럴과 함께 사용할 때 발생합니다 .

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

그 예기치 않은 행동은 이미 의 특징 이 아니었다with .

이 기술을 실제로 사용하려면 최소한 프로토 타입이 null 인 객체를 사용하십시오.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

그러나 이것은 ES5 +에서만 작동합니다. 또한 사용하지 마십시오 with.


0

사용자가 응용 프로그램의 일부 동작을 수정하기 위해 코드를 업로드 할 수있는 프로젝트를 진행 중입니다. 이 시나리오에서는 with코드를 사용하여 엉망으로 만드는 범위 밖의 항목을 수정하지 않도록 절을 사용했습니다 . 이 작업을 수행하는 데 사용하는 코드의 단순화 된 부분은 다음과 같습니다.

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

이 코드는 사용자 정의 코드가 다음과 같은 전역 범위의 객체에 액세스 할 수 없도록합니다. window 가 클로저를 통해 변수 나 로컬 변수에 .

현명한 한마디로, 사용자가 제출 한 코드에 대해 정적 코드 검사를 수행하여 다른 범위를 사용하여 전역 범위에 액세스하지 않는지 확인해야합니다. 예를 들어 다음 사용자 정의 코드는 다음에 대한 직접 액세스 권한을 갖습니다 window.

test = function() {
     return this.window
};
return test();


0

나의

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

~로 비등하다

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

품질이 낮은 코드를 믿을 수 있습니까? 아니요, 우리는 그것이 완전히 읽을 수 없도록 만들어졌습니다. 이 예제는 내가 가독성을 올바르게 가지고 있다면 with-statement가 필요 없다는 것을 명백히 증명합니다.)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.