JavaScriptの「with」ステートメントの正当な用途はありますか?


369

with声明に関する私の回答に対するアラン・ストームのコメントは私に考えさせられました。私はこの特定の言語機能を使用する理由をめったに見つけず、それがどのように問題を引き起こす可能性があるかについてあまり考えたことはありませんでした。さて、私はwithその落とし穴を避けながら、私がをどのように効果的に使用できるかについて興味があります。

このwithステートメントはどこで役に立ちましたか?


52
私はそれを使用することはありません。私はそれが存在しないふりをする場合、それなしで生活する方が楽です。
Nosredna、2009年

6
かつては多くの有効な用途があった可能性があります。しかし、それは愚かです。ES5 Strictが削除されたwithため、そのようなものはなくなりました。
Thomas Aylott、2010

27
ここで、ES5 Strictはオプションであることに注意してください。
Shog9

5
ES5 strictで「with」を削除する代わりに、標準が変更され、変数が見つからない場合、「with」内で行われた割り当てが引数オブジェクトにバインドされるようにした方がいいのではないでしょうか。
JussiR、2011年

2
@JussiR:たぶん。しかし、それを行う際の問題は、それが古いブラウザーで物事を壊す可能性があることです。
Sune Rasmussen

回答:


520

今日、別の使用法が私に起こりました。私は興奮してWebを検索し、それに関する既存の言及を見つけました:ブロックスコープ内の変数の定義

バックグラウンド

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-値2-が3つの関数すべてで共有されます。

新しいスコープ:letwith

ES6でのletステートメントの導入により、これらの問題を回避する必要がある場合に、新しいスコープを簡単に導入できます。

// 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の値を持つ3つの別々の変数を作成します。ブロック内で宣言さた変数は、C ++のブロックの動作とは異なり、スコープされないことに注意してください(Cでは、変数は最初に宣言する必要がありますブロックなので、同じように)。この動作は実際には、以前のバージョンのMozillaブラウザーで導入されたletブロック構文に非常に似ていますが、他の場所では広く採用されていません。


15
リテラルと一緒に使用することを考えたことはありませんが、正当なようです。
Matt Kantor

81
これは本当に本当に終わりました。この方法でJavaScriptのスコープを操作することを考えたことはありません。私のコーディングに新しい領域を完全に拡大しました。私は10回賛成できればいいのに!
kizzx2 2009年

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 2010

24
聞かせて IE内のステートメントのサポートは本当に今、私のベーコンを救う、私が使用するかどうかについて、私の良心に苦しんだ代わりに。本当の問題は、プロトタイプチェーン上のオブジェクトのプロパティが継承されているため、with as letを使用しても、特別な注意が必要なことです。たとえば、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
これの欠点は、マークアップビルダーオブジェクトの外にあるwithブロック内の変数を参照する場合、jsエンジンが最初にマークアップビルダー内で変数を検索するため、パフォーマンスが低下することです。
Adam Thomas

3
これは、キャンバスパスを操作するユーザーのコードを削減するのに役立ちます。
Brian McCutchon 2013

4
そのコードの「with」バージョンは、私の「non-with」バージョンよりも文字通り240倍以上遅いマシンで実行されます。これが、正当な使用法がないと人々が言う理由です。一部の場所でコードをきれいにできないためではありません。ベンチマークを参照してください:jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy-これはまさに、実行速度が遅いコードを使用してはならないタイプの場所です(このコードの上で作成したコメントを参照してください)。超繰り返しコードのショートカットが必要な場合は、それらを宣言できます。たとえばcontext.bezierCurveTo、100回ストレートを使用している場合は、発声var bc2 = context.bezierCurveTo;した後bc2(x,x,etc);、発呼するたびに移動します。これは非常に高速で、冗長度は低くwithなりますが、超低速です。
神保ジョニー2015年

83

私の以前のコメントが示したように、withどのような状況でもそれがどんなに魅力的であっても、安全に使用できるとは思いません。この問題はここでは直接取り上げられていないので、繰り返します。次のコードを検討してください

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

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

これらの関数呼び出しを注意深く調査しない限り、このコードの実行後のプログラムの状態を知る方法はありません。user.name既に設定されている場合は、になりますBob。設定されていない場合、グローバルnameは初期化または変更されBobuserオブジェクトはnameプロパティなしで残ります。

バグが発生します。一緒に使用すると、最終的にはこれが行われ、プログラムが失敗する可能性が高くなります。さらに悪いことに、意図的に、または作成者がこの構造の奇妙な点を知らない場合に、withブロックでグローバルを設定する作業コードに遭遇する場合があります。これは、スイッチでフォールスルーが発生するのによく似ています。作成者が意図したとおりであるかどうかはわかりません。また、コードを「修正」すると回帰が発生するかどうかを知る方法がありません。

現代のプログラミング言語は機能が満載です。長年の使用の結果、一部の機能は不良であることが判明し、回避する必要があります。JavaScript withはその1つです。


18
この問題は、オブジェクトの属性に値を割り当てるときにのみ発生します。しかし、値を読み取るためだけに使用している場合はどうでしょうか?その場合は使用しても大丈夫だと思います。
Airportyh

10
同じ問題がTobyの値の読み取りにも当てはまります。上記のコードスニペットでは、ユーザーオブジェクトに名前が設定されているかどうかがわからないため、グローバル名とユーザー名のどちらを読んでいるかはわかりません。
アランストーム

12
値の読み取りには、明確な優先規則があります。オブジェクトの属性は、スコープ外の変数の前にチェックされます。これは、関数内でスコープする変数と何の違いもありません。割り当てと 'with'の実際の問題は、私が理解しているように、属性割り当てが発生するかどうかは、属性が現在の問題のオブジェクトに存在するかどうかに依存するという事実にあります。これはランタイムプロパティであり、簡単に推定することはできません。コードを見てください。
airportyh

1
あなたはそこにトビーがいるかもしれません。書き込みの問題は、私が構造から完全に遠ざかるのに十分です。
アランストーム

「スイッチでフォールスルーに遭遇するのとよく似ています。何もわかりません...」 -では、switch()も禁止しましょう。;-p
Sz。

66

私は実際、このwith発言が最近非常に役に立ったと感じました。この手法は、JavaScriptで記述されたコマンドラインコンソールである現在のプロジェクトを開始するまで、実際には思いつきませんでした。Firebug / WebKitコンソールAPIをエミュレートしようとしていましたが、コンソールに特別なコマンドを入力できますが、グローバルスコープの変数はオーバーライドされません。Shog9の優れた答えに対するコメントで述べた問題を克服しようとするとき、私はこれを考えました。

この効果を達成するために、2つの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

@アダム:それについてはよくわかりません。ES5は、厳密モードでのみこれに対してエラーをスローするため、厳密モードがグローバルに宣言されていない場合は、すぐには問題になりません。ES Harmonyはより大きな問題を引き起こす可能性がありますが、プロキシなどの新しいもので解決できる場合があります。
Andy E

@AndyE申し訳ありませんが、これはトピック外ですが、「JavaScriptで記述されたコマンドラインコンソール」はどこでも利用できますか?
kybernetikos

@アダム:いいえ、そうではありません。全体は、Windowsデスクトップガジェットの開発者向けツールのセットを意図したものでしたが、完成することはありませんでした(ただし、コンソールは非常にうまく機能しています)。現時点では、WDGには明るい未来はありませんが、私はいつかそれを終えるかもしれません。
アンディE

3
数週間前に、ブロックでいくつかのES6機能をブロックしたため、Chromeのコンソール実装をブロックありからシンボルマジックに移動しました:)
Alexey Kozyatinskiy

54

はい、はい、はい。非常に正当な使用があります。見る:

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

基本的に、他のDOMまたはCSSフックはwithの素晴らしい使用法です。「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からの単純なメソッドを使用して、同じことを1行で実現できます$.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'})
Trevor Burnham

9
@TrevorBurnham-jQueryが利用可能であると想定する場合は、その.css()メソッドを使用するだけです...
nnnnnn

4
これらの変数がグローバルスコープに移動するのを妨げているのは何ですか?すべてのCSSスタイルが常にすべての要素で定義されているからでしょうか、それとも何ですか?
mpen

1
@マークはい、それらは常に定義され、プロパティにカスタムスタイルがない場合はnullまたは空の文字列が値として使用されます
Esailija

34

小さなヘルパー関数を定義してwith、あいまいさのない利点を提供できます。

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

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

8
OMG、私の頭が爆発しました!あいまいさなしで?お奨め、投票してください!
ジャロッドディクソン

@Jarrod:これについて何がそんなに面白いのですか(is.gd/ktoZ)?このサイトを利用するほとんどの人は私より賢いので、私が間違っていても許してください、しかしこれは悪い情報のようです。
レイヴン

14
しかし、それはより長く、実行方法を理解するのが難しいだけです:var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo 2009年

3
ルネ:これで、 "_"変数を外部スコープに公開し、潜在的なバグが発生します。また、オブジェクトパラメータの計算に使用される一時変数も公開します。
John Millikin

30
with_泥だらけの二重バージョン(function(_){ _.a="foo"; })(object_here);(c / javaスタイルのブロックをシミュレートする標準的な方法)になると、さらに多くのバグが発生します。代わりにそれを使用してください。
mk。

25

次のことができるので、それだけの価値はないようです。

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

1
これは私には意味がありませんでした。私には、JavaScriptの専門家ではないが
JeroenEijkhof

@WmasterJ明確にするために、この投稿を参照してください:yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis

8
長い変数名だけがの使用例ではありませんwith
クリス

18

私はこれまでに使用したことがなく、理由も見たことがなく、推奨もしていません。

の問題withは、ECMAScript実装が実行できる多くの字句の最適化を妨げることです。高速JITベースのエンジンの台頭を考えると、この問題は近い将来さらに重要になるでしょう。

それはwithよりきれいな構造を可能にするように見えるかもしれませんが(たとえば、一般的な匿名関数ラッパーの代わりに新しいスコープを導入するか、詳細なエイリアスを置き換えるとき)、それは 本当に価値がありません。パフォーマンスの低下に加えて、間違ったオブジェクトのプロパティに割り当てられる(注入されたスコープ内のオブジェクトにプロパティが見つからない場合)可能性があり、おそらく誤ってグローバル変数を導入する危険があります。IIRC、後者の問題は、Crockfordが回避することを推奨する動機となった問題ですwith


6
パフォーマンスボギーマンは、グローバルなものとほぼ同じ頻度で頻繁に追い出されます...私たちが話しているJavaScriptであることを考えると、常に奇妙な印象を受けます。あなたはパフォーマンスへの影響が非常に劇的であると思いますが、多くの注意を払う必要がありますが... with(){}ここの他の回答で示されているような構成のコストについて、最新のブラウザで具体的な数値がある場合は、ぜひご覧くださいそれら!
Shog9 2009

6
JavaScriptのコンテキストでなぜ奇妙なのですか?:)そして、はい、それは劇的です。考えてみてください-実装では、括弧内の式を評価し、それをオブジェクトに変換し、現在のスコープチェーンの前に挿入し、ブロック内のステートメントを評価して、スコープチェーンを通常に戻す必要があります。それは大変な作業です。高度に最適化された低レベルコードに変換できる単純なプロパティルックアップだけではありません。ここに私が作った非常に単純なベンチマークがあります(間違いを見つけたら知らせてください)-gist.github.com/c36ea485926806020024
kangax

5
@kangax:私はC ++の出身です。大規模なルーチンやプログラムのパフォーマンスに目立った影響がない場合でも、多くのプログラマーがコードの小さな効率にこだわるのは伝統的なことです。JavaScriptのコンテキストでは、奇妙に思えます。そのようなルーチンのパフォーマンスの大部分は、VMの実装に依存している可能性があります。JSプログラマーがセットアップコストへの懸念のために匿名関数などを回避するいくつかの例を見てきましたが、これは例外ではなく、コードの非常に機密性の高い領域用に予約されています。
Shog9 2009

5
そうは言っても、次のコストに関しては完全に正しいです。with(){}新しいスコープの設定は、withテストしたすべてのブラウザーで非常に高価です。非常に頻繁に呼び出されるコードでは、これを回避する必要があります。さらに、Chromeはwith()スコープ内で実行されるコードに対して劇的なヒットを示しました。興味深いことに、IEはwith()ブロック内のコードに対して最高のパフォーマンス特性を備えていました。セットアップコストを考慮してwith()、IE6およびIE8 VMでメンバーアクセスの最速の手段を提供します(これらのVMは全体的に最も遅いです)。良いもの、ありがとう...
Shog9

5
FWIW:これは、セットアップコストを考慮した同じテストセットです。jsbin.com / imidu / editの変数アクセスwith()は、Chromeではほぼ1桁遅く、IEでは2倍以上高速です...!
Shog9 2009

13

Visual Basic.NETにも同様のWithステートメントがあります。私が使用する最も一般的な方法の1つは、いくつかのプロパティをすばやく設定することです。の代わりに:

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

、私は書くことができます:

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

これは怠惰の問題だけではありません。また、より読みやすいコードになります。また、JavaScriptとは異なり、ステートメントの影響を受けるすべての要素の前に.(ドット)を付ける必要があるため、あいまいさはありません。したがって、次の2つは明確に区別されます。

With someObject
    .Foo = ''
End With

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

読みやすさや表現力を優先するかどうかにもよると思います。

最初の例はより読みやすく、おそらくほとんどのコードに推奨されます。しかし、ほとんどのコードはとにかくかなり使いこなされています。2つ目はもう少しあいまいですが、言語の表現の性質を使用して、コードサイズと余分な変数を削減します。

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コードを見るのは素晴らしい!JavaScriptの「with」は、スキームのルーツとしての言語に触発されたのは明らかだと思いますが、残念ながら、JavaScriptの質問にLISPを投稿することに反対しています。
Fire Crow

1
基本原則に賛成票を投じます。「with」は驚くほど強力な構造です。しかし、ほとんどのJSの人々はクロージャーを理解せず、JSの上に途方もなく複雑なJavaクラス継承システムを記述します-では、「with」が提供するメタプログラミングの可能性をどのようにして認識できるでしょうか?
2010

もちろんwith-slotswithどのスロットを使用するかを指定する必要がありますが、実行時にバインドされたスロットを使用します。
Samuel Edwin Ward

6

Delphiでの経験を持って、私が使用していることを言うだろうとすると、おそらく、その安全性を確認するために、静的コード解析へのアクセス権を持つJavaScriptの最小化アルゴリズムのいくつかの種類によって実行される、最後の手段サイズを最適化する必要があります。

withステートメントを自由に使用することで発生する可能性のあるスコープの問題は、a **に大きな影響を与える可能性があります。コードで何が起こっているのかを理解するために、デバッグセッションを体験してほしくありません。 、意図したグローバルまたは外部スコープ変数ではなく、オブジェクトメンバーまたは誤ったローカル変数をキャプチャしたことを確認するためだけです。

VB withステートメントはスコープを明確にするためにドットが必要であるという点で優れていますが、Delphi withステートメントはヘアトリガーを備えたロードされたガンであり、JavaScriptは同じ警告を保証するのに十分似ているように見えます。


5
ステートメント付きのjavascriptは、Delphiのものよりも劣ります。Delphiでは、withは、object.member表記法と同じくらい高速です(高速でない場合)。JavaScriptでは、withはスコープを歩いて一致するメンバーをチェックする必要があるため、常にobject.member表記よりも遅くなります。
Martijn、

5

withの使用は推奨されておらず、ECMAScript 5のstrictモードでは禁止されています。推奨される代替方法は、一時変数にアクセスするプロパティを持つオブジェクトを割り当てることです。

出典: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」など、よく使用される特定のヘルパー関数を追加できます。

頻繁に読むパフォーマンスの低下について:関数のスコープを指定してもパフォーマンスに影響はありません。実際、私の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でを使用する正当な理由はありません。


5
時期尚早の最適化。数値を計算しない限り、「遅い」とは言わないでください。オーバーヘッドは、現代と古代の両方のjs実装で取るに足らないものです。
mk。

2
あなた以外の開発者にとって問題ではないかもしれないことに基づいて、私はあなたの結論に強く同意しません。
Dave Van den Eynde、2010年

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;平均で2,500、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倍以上遅くなります。
ヨリック

3
これを見たとき、コンソールのChrome 23でこれらを実行しただけです。私が得た結果は、withコードで1138、なしで903でした。タイトループでもこの小さな違いがあるので、パフォーマンスを心配する前に、コーディングの単純さとケースバイケースでのリファクタリングの容易さに基づいて選択を行います。
Plynx 2013年

3

テンプレート言語をJavaScriptに変換するときにwith文が役立つと思います。たとえば、base2のJSTですが、もっと頻繁に見ました。

with-statementなしでこれをプログラムできることに同意します。しかし、それは何の問題も与えないので、それは合法的な使用です。


3

比較的複雑な環境で実行されるコードをコンテナーに入れるのに適しています。これを使用して、「ウィンドウ」などのローカルバインディングを作成し、Webブラウザー向けのコードを実行します。


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

一部の短いコードでは、放射モードではなく、度モードsincosなどの三角関数を使用したいと思います。この目的のために、私は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この方法でステートメントを使用するのは良い考えではありません。どの関数がグローバルであり、どの関数が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せるだろういくつかの使用法をあなたに示すことができるとかなり確信しています。
ファンメンデス

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();
}

逆に、あなたがデメテル法則に違反していると主張することはできますが、もう一度、多分そうではありません。余談です=)。

何よりも、Douglas Crockford を使用しないことをお勧めしwithます。ここで彼のブログ投稿withとその代替案をチェックすることをお勧めします。


返事をありがとう、トム。私はクロックフォードの推薦を読みました、そしてそれは理にかなっていますが、それはそれまでしか行きません。私は、doekmanによって間接的に触れられたアイデアに近づいています。with{}の本当の力は、スコープを操作するために使用できる方法にある...
Shog9

2

with。を使用する方法が、object.memberと入力するよりも読みやすいことは本当にわかりません。読みにくいとは思わないが、読みにくいとは思わない。

lassevkが言ったように、非常に明示的な「object.member」構文を使用するよりも、with withを使用するとエラーが発生しやすくなることが確実にわかります。


1

W3schools http://www.w3schools.com/js/js_form_validation.aspでJavaScriptのフォームの検証を見る必要がありますで「email」という名前の入力を見つけるためにオブジェクトフォームを「スキャン」する

しかし、どのフォームからでも取得できるように変更しました。フォーム内のフィールドの名前や数量に関係なく、すべてのフィールドが空ではないことが検証されます。さて、私はテキストフィールドのみをテストしました。

しかし、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

with。を使用すると、require.jsを使用するときにアリティを明示的に管理する必要がなくなります。

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.