js の with キーワードといえば、多くの友人の第一印象は、with キーワードの機能はスコープの変更であり、最も重要な点は with キーワードの使用は推奨されないということでしょう。 with キーワードは推奨されないと聞くと、多くの人は with キーワードを無視して、無視して使用すればよいと考えます。しかし、コードや面接の質問を見ると、with キーワードに関連した質問が出てくることがあります。これまで経験したことのない落とし穴がたくさんあるため、with キーワードについて話しておく必要があります。
var obj = { a: 1, b: 2, c: 3};
// 重复写了3次的“obj”obj.a = 2; obj.b = 3; obj.c = 4;
with (obj) { a = 3; b = 4; c = 5; }
function foo(obj) { with (obj) { a = 2; } }var o1 = { a: 3};var o2 = { b: 3} foo(o1); console.log(o1.a); //2foo(o2); console.log(o2.a); //underfinedconsole.log(a); //不好,a被泄漏到全局作用域上了
foo(obj)
関数は、オブジェクト参照である obj の仮パラメータを受け入れ、オブジェクトに対して with(obj) {...}
を実行します。 with ブロック内には、実際には LHS 参照である a への字句参照があり、それに 2 が割り当てられます。 o1 を渡すと、a = 2
代入操作で o1.a が見つかり、それに 2 が代入されます。 o2 が渡されると、o2 にはプロパティがないため、このプロパティは作成されず、o2.a は未定義のままになります。
foo(obj)
函数接受一个 obj 的形参,该参数是一个对象引用,并对该对象医用执行了 with(obj) {...}
。在 with 块内部,对 a 有一个词法引用,实际上是一个 LHS引用,将 2 赋值给了它。
当我们将 o1 传递进去,a = 2
赋值操作找到了 o1.a 并将 2 赋值给它。而当 o2 传递进去,o2 并没有 a 的属性,因此不会创建这个属性,o2.a 保持 undefined。
但为什么对 o2的操作会导致数据的泄漏呢?
这里需要回到对 LHS查询
的机制问题(详情可移步 JavaScript中的LHS和RHS查询)。
当我们传递 o2 给 with 时,with 所声明的作用域是 o2, 从这个作用域开始对 a 进行 LHS查询
。o2 的作用域、foo(…) 的作用域和全局作用域中都没有找到标识符 a,因此在非严格模式下,会自动在全局作用域创建一个全局变量),在严格模式下,会抛出ReferenceError
しかし、なぜo2の運用が情報漏洩につながるのでしょうか?
LHS クエリ
のメカニズムに戻る必要があります (詳細については、JavaScript の LHS クエリと RHS クエリを参照してください)。 o2 を with に渡すと、with で宣言されたスコープは o2 となり、a のwithが推奨されないもう1つの理由は、です。厳密モードでは、eval(…) の間接的または安全でない使用と同様に、with は完全に禁止されています。LHS クエリ
はこのスコープから開始されます。識別子 a は、o2 のスコープ、foo(...) のスコープ、およびグローバル スコープでは見つからないため、非厳密モードでは、グローバル変数がグローバル スコープに自動的に作成されます。スコープ)、厳密モードでは、ReferenceError
例外がスローされます。
パフォーマンスの低下
答えは「ノー」です。具体的な理由については、まずコードの次の部分を見てみましょう。次のコードをコピーして直接実行できます
<script>function func() { console.time("func"); var obj = { a: [1, 2, 3] }; for(var i = 0; i < 100000; i++) { var v = obj.a[0]; } console.timeEnd("func"); } func();function funcWith() { console.time("funcWith"); var obj = { a: [1, 2, 3] }; with(obj) { for(var i = 0; i < 100000; i++) { var v = a[0]; } } console.timeEnd("funcWith"); } funcWith();</script>
そして、テスト効果:
同じロジックを処理するコードの実行時間は、ありなしの場合はわずか 4.63 ミリ秒です。 with使用時の使用時間は81.87msと長いです。
これはなぜですか?
その理由は、JavaScript エンジンがコンパイル段階でいくつかのパフォーマンスの最適化を実行するためです。これらの最適化の一部は、コードをその語彙集に基づいて静的に分析し、すべての変数と関数が定義されている場所を事前に決定して、実行中に識別子をすぐに見つけられるようにすることに依存しています。
しかし、エンジンがコード内で with を見つけた場合、新しい字句スコープの作成に使用された with に渡されたオブジェクトの内容を知る方法がないため、識別子の位置に関する判断は無効であると単純に想定できます。は。
最も悲観的なケースは、with が出現した場合、すべての最適化が無意味になる可能性があるということです。したがって、エンジンが採用する最も単純なアプローチは、最適化をまったく行わないことです。コードで with または eval() を多量に使用すると、間違いなく実行が非常に遅くなります。エンジンがこれらの悲観的な状況による副作用を最小限に抑えようとしてどれほど賢明であっても、これらの最適化がなければコードの実行が遅くなるという事実を避けることはできません 。 この記事では、JavaScript での with の使用法について説明します。その他の関連コンテンツについては、php 中国語 Web サイトを参照してください。
関連する推奨事項:
js と php のネストjs はデフォルトのイベントを防ぎ、js はバブリングイベントをブロックする例を共有します js 関数の一般的な書き込みメソッドと呼び出しメソッド以上がJavaScriptでのwithの使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。