暗黙的なグローバル変数
ローカル変数
変数宣言のホイスティング(ホイスティング)
名前解決の順序
名前空間
結論
JavaScript は For コード セグメントをサポートしていますが、中括弧で作成される場合、ブロックレベルのスコープはサポートされません。関数スコープのみがサポートされます。
function test() { // 一个作用域 for(var i = 0; i < 10; i++) { // 不是一个作用域 // count } console.log(i); // 10 }
翻訳者注: return オブジェクトの左括弧と return が同じ行にない場合、エラーが発生します。
(注: 代入ステートメント内ではなく、戻り式または関数パラメーター内にある場合、{...} はオブジェクトのリテラル構文としてではなく、コード セグメントとして解析されます。自動解析が有効な場合、 No. の挿入を考慮すると、これによりいくつかの微妙なエラーが発生する可能性があります)
// 译者注:下面输出 undefined function add(a, b) { return a + b; } console.log(add(1, 2));
JavaScript には明示的な名前空間定義がありません。これは、すべてのオブジェクトがグローバルに共有される名前空間の下で定義されることを意味します。
変数が参照されるたびに、JavaScript は変数が見つかるまでスコープ全体を上方向に走査します。 グローバル スコープに到達しても変数が見つからない場合は、ReferenceError 例外がスローされます。
暗黙的なグローバル変数
// 脚本 A foo = '42'; // 脚本 B var foo = '42'
上記の 2 つのスクリプトには異なる効果があります。スクリプト A はグローバル スコープで変数 foo を定義し、スクリプト B は現在のスコープで変数 foo を定義します。
もう一度言いますが、変数の宣言に var を使用しない場合は、暗黙的なグローバル変数が生成されます。
// 全局作用域 var foo = 42; function test() { // 局部作用域 foo = 21; } test(); foo; // 21
関数テスト内で var キーワードを使用せずに foo 変数を宣言すると、同じ名前の外部変数が上書きされます。 最初はこれは大きな問題に思えないかもしれませんが、コードが数千行ある場合、var を使用せずに変数を宣言すると、追跡が難しいバグが発生する可能性があります。
// 全局作用域 var items = [/* 数组 */]; for(var i = 0; i < 10; i++) { subLoop(); } function subLoop() { // subLoop 函数作用域 for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 // 干活 } }
subLoop はグローバル変数 i を上書きするため、外側のループは subLoop への最初の呼び出し後に終了します。 このエラーは、2 番目の for ループで var を使用して変数を宣言することで回避できます。 外部スコープに影響を与える望ましい動作でない限り、変数を宣言するときに var キーワードを省略しないでください。
ローカル変数
JavaScript のローカル変数は 2 つの方法でのみ宣言できます。1 つは関数パラメーターとして宣言され、もう 1 つは var キーワードを通じて宣言されます。
// 全局变量 var foo = 1; var bar = 2; var i = 2; function test(i) { // 函数 test 内的局部作用域 i = 5; var foo = 3; bar = 4; } test(10);
foo と i は関数 test 内のローカル変数であり、bar への代入により、グローバル スコープ内の同じ名前の変数が上書きされます。
変数宣言のホイスト(Hoisting)
JavaScriptは変数宣言をホイスティングします。これは、var 式と関数宣言の両方が現在のスコープの先頭に巻き上げられることを意味します。
bar(); var bar = function() {}; var someValue = 42; test(); function test(data) { if (false) { goo = 1; } else { var goo = 2; } for(var i = 0; i < 100; i++) { var e = data[i]; } }
上記のコードは実行前に変換されます。 JavaScript は、var 式と関数宣言を現在のスコープの先頭に巻き上げます。
// var 表达式被移动到这里 var bar, someValue; // 缺省值是 'undefined' // 函数声明也会提升 function test(data) { var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部 if (false) { goo = 1; } else { goo = 2; } for(i = 0; i < 100; i++) { e = data[i]; } } bar(); // 出错:TypeError,因为 bar 依然是 'undefined' someValue = 42; // 赋值语句不会被提升规则(hoisting)影响 bar = function() {}; test();
ブロック スコープがないことにより、var 式がループの内側から外側に移動されるだけでなく、一部の if 式が読みにくくなります。
元のコードでは、if 式はグローバル変数 goo を変更しているように見えますが、実際にはプロモーション ルールが適用された後にローカル変数を変更します。
ホイスティングの知識がないと、次のコードは ReferenceError 例外をスローするように見えます。
// 检查 SomeImportantThing 是否已经被初始化 if (!SomeImportantThing) { var SomeImportantThing = {}; }
実際、var 式がグローバル スコープの最上位にホイストされているため、上記のコードは正常に動作します。
var SomeImportantThing; // 其它一些代码,可能会初始化 SomeImportantThing,也可能不会 // 检查是否已经被初始化 if (!SomeImportantThing) { SomeImportantThing = {}; }
翻訳者注: Nettuts+ Web サイトにホイスティングを紹介する記事があり、そのコードは非常に啓発的です。
// 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则 var myvar = 'my value'; (function() { alert(myvar); // undefined var myvar = 'local value'; })();
名前解決の順序
グローバル スコープを含む JavaScript のすべてのスコープには、現在のオブジェクトを指す特別な名前があります。関数スコープにはデフォルトの変数引数もあり、これには関数に渡されるパラメータが含まれます。たとえば、関数内の foo 変数にアクセスすると、JavaScript は次の順序で検索します:
現在のスコープに var foo の定義があるかどうか。
関数の仮パラメータが foo 名を使用するかどうか。
関数自体が foo と呼ばれるかどうか。
前のスコープに戻り、#1 からやり直します。
名前空間
グローバル スコープが 1 つしかないことによって引き起こされるよくある間違いは、名前の競合です。 JavaScript では、これは匿名ラッパーを使用して簡単に解決できます。
(注: カスタム引数パラメーターはネイティブ引数オブジェクトの作成を防ぎます。)
(function() { // 函数创建一个命名空间 window.foo = function() { // 对外公开的函数,创建了闭包 }; })(); // 立即执行此匿名函数
匿名関数は式とみなされ、呼び出し可能性を考慮して最初に実行されます。
( // 小括号内的函数首先被执行 function() {} ) // 并且返回函数对象 () // 调用上面的执行结果,也就是函数对象
関数式を呼び出す方法は他にもいくつかあります。たとえば、次の 2 つのメソッドは構文が異なりますが、結果はまったく同じです。
// 另外两种方式 +function(){}(); (function(){}());
結論
名前空間を作成するには、匿名ラッパー (翻訳者注: 自己実行匿名関数) を使用することをお勧めします。これにより、名前の競合が防止されるだけでなく、プログラムのモジュール化も容易になります。
また、グローバル変数の使用は悪い習慣とみなされます。このようなコードはエラーが発生しやすく、保守にコストがかかります。
上記は JavaScript 上級シリーズ - スコープと名前空間の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。