JavaScript は、緩い言語として、多くの驚くべき機能 (多くの場合、とらえどころのない奇妙な機能) を備えています。この記事では、JavaScript の機能を使用して「スコープの牢獄」を突破する方法を紹介します。従来のプログラミング言語の「」。
jsには変数宣言促進と関数宣言促進という特徴があることはご存知の方も多いはずです。以前に理解したかどうかに関係なく、次のコードの実行結果が期待どおりかどうかを確認してください:
var a=123; //可以运行 abc(); //报错:def is not a function def(); function abc(){ //undefined console.log(a); var a="hello"; //hello console.log(a); } var def=function(){ console.log("def"); }
実際、js は実行時にコードに対して 2 ラウンドのスキャンを実行します。最初のラウンドでは変数が初期化され、2 番目のラウンドではコードが実行されます。 2 ラウンド目の実行コードは理解しやすいですが、1 ラウンド目のプロセスはより曖昧です。具体的には、最初のラウンドでは次の 3 つのことを行います:
(1) 関数パラメーターを宣言して初期化します
(2) ローカル変数を宣言します (匿名関数をローカル変数に割り当てることを含みますが、初期化はしません)
(3) 宣言しますそして関数を初期化します
これらの理論的基礎を理解した後、最初のスキャンの後に、上記のコードは実際に js コンパイラーによって次のコードに「変換」されました:
var a; a=123; function abc(){ //局部变量,将会取代外部的a var a; //undefined console.log(a); var a="hello"; //hello console.log(a); } var def; //可以运行 abc(); //报错:def is not a function def(); var def=function(){ console.log("def"); }
次に、コメントに示されている内容を見てください。プログラムが実行されているときは論理的だと思いますか?これがjs宣言の促進が担う役割です。
js 宣言のプロモーションのメカニズムを理解した後、次のコードを見てみましょう:
var obj={}; function start(){ //undefined //This is obj.a console.log(obj.a); //undefined //This is a console.log(a); //成功输出 //成功输出 console.log("页面执行完成"); } start(); var a="This is a"; obj.a="This is obj.a"; start();
上記のコメントの 1 行目は start() メソッドが初めて実行されたときの出力を表し、2 行目は2 回目の実行。start() メソッドの出力。 js 宣言のプロモーションの存在により、start() メソッドが 2 回実行されてもエラーが報告されなかったことがわかります。この例の小さな変更を見てみましょう:
var obj={}; function start(){ //undefined //This is obj.a console.log(obj.a); //报错 //This is a console.log(a); //因为上一行的报错导致后续代码不执行 //成功输出 console.log("页面执行完成"); } start(); /*---------------另一个js文件----------------*/ var a="This is a"; obj.a="This is obj.a"; start();
この時点では、変数の宣言が別の js ファイルに延期されるため、console.log(a) コードは初めて実行されるときにエラーを報告します。その後の js コードは実行されなくなります。ただし、start() メソッドは 2 回目でも通常どおり実行されます。これが、ほとんどの場所で、さまざまな js ファイルをデプロイするために「js 名前空間」を使用することが推奨される理由です。以下では、コードの一部を使用して、宣言のプロモーション + 名前空間がどのように賢く「アクションの檻を打ち破る」ことができるかを要約しています:
/*-----------------第一个js文件----------------*/ var App={}; App.first=(function(){ function a(){ App.second.b(); } return { a:a }; })(); /*-----------------另一个js文件----------------*/ App.second=(function(){ function b(){ console.log("This is second.b"); } return { b:b }; })(); //程序起点,输出This is second.b App.first.a();
このプログラムはエラーを報告せず、最初の js ファイル内の任意の App 名前空間にアクセスできます 後続のプロパティが存在します必要な割り当てをすべて行った後にプログラムの開始点が実行される限り、問題はありません。この例は、コード構造の合理的な設計を通じて、js 言語の動的な特性を最大限に活用する方法をうまく示しています。
読者は、この記事のタイトルが少しおかしいと感じるかもしれません。上記のテクニックはコード レイアウトによって作られた単なる「幻想」であり、実際には存在しないプロパティに前のコードがアクセスしているように見えます。実行されました。その命令はすべて合理的で正しいものです。そこでこの記事では、実際の「クロスアクションアクセス」テクニックを紹介します。
js言語には「檻を本当に壊す」代表的なメソッドである「eval()」メソッドがあることは皆さんご存知でしょう。以下のコードを見てください:
(function(){ var code="console.log(a)"; //This is a bird test(code); function test(code){ console.log=function(arg){ console.info("This is a "+arg); }; var a="bird"; eval(code); } })();
このコードを読んだ後、多くの人は「これでも動くの?!」と js の素晴らしさにため息をつかずにはいられないでしょう。はい。 test() メソッドは、宣言プロモーション メカニズムにより、事前に呼び出して通常どおり実行できます。 test() メソッドはコード パラメータを受け入れます。test() メソッド内で console.log メソッドを書き換え、出力形式を変更し、テスト内にプライベート変数 var a="bird" を定義しました。テスト メソッドの最後で、eval を使用してコードを動的に実行します。出力結果は非常に魅力的です。ブラウザは、テスト メソッド内のプライベート変数 a を出力するために書き直した console.log メソッドを使用します。これは完全なスコープの分離です。
js には、eval()、setTimeout()、setInterval() やネイティブ オブジェクトのいくつかの構築メソッドなど、同様のメソッドが多数あります。ただし、次の 2 つの点に注意してください:
(1) この方法では、プログラムの実行効率が大幅に低下します。 js 自体がインタープリター言語であり、そのパフォーマンスがコンパイル言語よりもすでに何段階も遅いことは誰もが知っています。これに基づいて、eval などのメソッドを使用して文字列コードを「再コンパイル」すると、プログラムのパフォーマンスが大幅に低下します。
(2) この方法でプログラミングすると、コードの複雑さが大幅に増し、作成したコードを数分で理解できなくなります。この記事では、読者が js の文法的特徴を包括的に理解し、修正やトラブルシューティングをより適切に行えるようになることを願って、この方法を紹介します。この記事では、運用レベルのコードで 2 番目のアプローチを使用することはまったく推奨しません。
上記はスコープの檻を突破した JavaScript の内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。