ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript アドバンスト シリーズ - スコープと名前空間

JavaScript アドバンスト シリーズ - スコープと名前空間

黄舟
黄舟オリジナル
2017-02-08 09:35:361310ブラウズ
  • 暗黙的なグローバル変数

  • ローカル変数

  • 変数宣言のホイスティング(ホイスティング)

  • 名前解決の順序

  • 名前空間

  • 結論

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 = &#39;42&#39;;

// 脚本 B
var foo = &#39;42&#39;

上記の 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; // 缺省值是 &#39;undefined&#39;

// 函数声明也会提升
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 依然是 &#39;undefined&#39;
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 = &#39;my value&#39;;  

(function() {  
    alert(myvar); // undefined  
    var myvar = &#39;local value&#39;;  
})();

名前解決の順序


グローバル スコープを含む JavaScript のすべてのスコープには、現在のオブジェクトを指す特別な名前があります。関数スコープにはデフォルトの変数引数もあり、これには関数に渡されるパラメータが含まれます。たとえば、関数内の foo 変数にアクセスすると、JavaScript は次の順序で検索します:

  1. 現在のスコープに var foo の定義があるかどうか。

  2. 関数の仮パラメータが foo 名を使用するかどうか。

  3. 関数自体が foo と呼ばれるかどうか。

  4. 前のスコープに戻り、#1 からやり直します。

名前空間

グローバル スコープが 1 つしかないことによって引き起こされるよくある間違いは、名前の競合です。 JavaScript では、これは匿名ラッパーを使用して簡単に解決できます。

(注: カスタム引数パラメーターはネイティブ引数オブジェクトの作成を防ぎます。)

(function() {
    // 函数创建一个命名空间

    window.foo = function() {
        // 对外公开的函数,创建了闭包
    };

})(); // 立即执行此匿名函数

匿名関数は式とみなされ、呼び出し可能性を考慮して最初に実行されます。

( // 小括号内的函数首先被执行
function() {}
) // 并且返回函数对象
() // 调用上面的执行结果,也就是函数对象

関数式を呼び出す方法は他にもいくつかあります。たとえば、次の 2 つのメソッドは構文が異なりますが、結果はまったく同じです。

// 另外两种方式
+function(){}();
(function(){}());

結論


名前空間を作成するには、匿名ラッパー (翻訳者注: 自己実行匿名関数) を使用することをお勧めします。これにより、名前の競合が防止されるだけでなく、プログラムのモジュール化も容易になります。

また、グローバル変数の使用は悪い習慣とみなされます。このようなコードはエラーが発生しやすく、保守にコストがかかります。

上記は JavaScript 上級シリーズ - スコープと名前空間の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。