8

「js」の関数には字句スコープがあることを理解しています(つまり、関数は、実行時ではなく定義時に環境(スコープ)を作成します)。

function f1() {
    var a = 1;
    f2();
}

function f2() {
    return a;
}
f1(); // a is not defined

「f()」だけを実行すると、内部関数が返されます。私が得たのは、それが 'return' の機能です!

function f() {
    var b = "barb";
    return function() {
        return b;
    }
}
console.log(b); //ReferenceError: b is not defined

「ReferenceError: b が定義されていません」というメッセージが表示されるのはなぜですか? しかし、上記の内部関数はそのスペース、f() のスペースなどにアクセスできません。「b」がグローバル スペースに返されているため、console.log() は機能しませんか?

ただし、「f()」を新しい変数に割り当てて実行すると、次のようになります。

 var x = f(); 
 x();// "barb"
 console.log(b); //ReferenceError: b is not defined

これは「barb」である「b」を返しますが、console.log() を再度実行すると、「ReferenceError: 'b' is not defined」が返されます。'b' が返されたので、グローバル スコープに含まれていませんか? では、なぜ 'x()' も 'f()' と同じように内部関数を返さなかったのでしょうか?

4

4 に答える 4

57

あなた、私の友人は完全に混乱しています。あなたの最初のステートメント自体が間違っています:

関数は、実行時ではなく定義時に環境 (スコープ) を作成します。

実際は逆です。関数を定義してもスコープは作成されません。関数を呼び出すと、スコープが作成されます。

スコープとは

簡単に言えば、スコープは変数の寿命です。ほら、すべての変数は生まれ、生き、そして死ぬ。スコープの開始は変数の発生時刻を示し、スコープの終了は変数の終了時刻を示します。

最初はスコープが 1 つしかありません (プログラム スコープまたはグローバル スコープと呼ばれます)。このスコープで作成された変数は、プログラムが終了したときにのみ消滅します。それらはグローバル変数と呼ばれます。

たとえば、次のプログラムを考えてみましょう。

const x = 10;       // global variable x

{                   // beginning of a scope
    const x = 20;   // local variable x
    console.log(x); // 20
}                   // end of the scope

console.log(x);     // 10

ここでは、 というグローバル変数を作成しましたx。次に、ブロック スコープを作成しました。このブロック スコープ内で、ローカル変数を作成しましたx。ローカル変数はログを記録するときにグローバル変数をシャドーxするため、 が得られ20ます。xログを取得すると、グローバルスコープに戻ります10(ローカルxは現在死んでいます)。

ブロック スコープと関数スコープ

現在、プログラミングには、ブロック スコープと関数スコープの 2 つの主なタイプのスコープがあります。

前の例のスコープはブロック スコープでした。それは単なるコードのブロックです。したがって、名前。ブロック スコープはすぐに実行されます。

一方、関数スコープはブロック スコープのテンプレートです。名前が示すように、関数スコープは関数に属します。ただし、より正確には、関数呼び出しに属します。関数スコープは、関数が呼び出されるまで存在しません。例えば:

const x = 10;

function inc(x) {
    console.log(x + 1);
}

inc(3);         // 4
console.log(x); // 10
inc(7);         // 8

ご覧のとおり、関数を呼び出すたびに新しいスコープが作成されます。4それが、出力、10およびを取得する理由です8

もともと、JavaScript には関数スコープしかありませんでした。ブロックスコープはありませんでした。したがって、ブロックスコープを作成したい場合は、関数を作成してすぐに実行する必要がありました:

const x = 10;         // global variable x

(function () {        // beginning of a scope
    const x = 20;     // local variable x
    console.log(x);   // 20
}());                 // end of the scope

console.log(x);       // 10

このパターンは、即時呼び出し関数式 (IIFE)と呼ばれます。もちろん、最近では と を使用してブロック スコープの変数を作成できconstますlet

レキシカルスコープとダイナミックスコープ

関数スコープは、レキシカルとダイナミックの 2 つのタイプに分けることができます。ご覧のとおり、関数には 2 種類の変数があります。

  1. 自由変数
  2. バインドされた変数

スコープ内で宣言された変数は、そのスコープにバインドされます。スコープ内で宣言されていない変数は自由です。これらの自由変数は他のスコープに属していますが、どのスコープですか?

レキシカルスコープ

レキシカルスコープでは、自由変数は親スコープに属していなければなりません。例えば:

function add(x) {         // template of a new scope, x is bound in this scope
    return function (y) { // template of a new scope, x is free, y is bound
        return x + y;     // x resolves to the parent scope
    };
}

const add10 = add(10);    // create a new scope for x and return a function
console.log(add10(20));   // create a new scope for y and return x + y

JavaScript は、ほとんどのプログラミング言語と同様に、レキシカル スコープを持っています。

動的スコープ

レキシカル スコープとは対照的に、動的スコープでは、自由変数は呼び出しスコープ (呼び出し関数のスコープ) に属している必要があります。例(これもJSではありません-動的スコープはありません):

function add(y) {   // template of a new scope, y is bound, x is free
    return x + y;   // x resolves to the calling scope
}

function add10(y) { // template of a new scope, bind y
    var x = 10;     // bind x
    return add(y);  // add x and y
}

print(add10(20));   // calling add10 creates a new scope (the calling scope)
                    // the x in add resolves to 10 because the x in add10 is 10

それでおしまい。シンプルですよね?

問題

最初のプログラムの問題は、JavaScript に動的スコープがないことです。レキシカルスコープしかありません。間違いがわかりますか?

function f1() {
    var a = 1;
    f2();
}

function f2() {
    return a;
}

f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)

あなたの 2 番目のプログラムは非常に混乱しています。

function f() {
    var b = "barb";

    return function() {
        return b;
    }
}

console.log(b); //ReferenceError: b is not defined

ここに間違いがあります:

  1. に電話したことはありませんf。したがって、変数bは作成されません。
  2. f変数を呼び出したとしても、bローカルになりfます。

これはあなたがする必要があることです:

function f() {
    const b = "barb";

    return function() {
        return b;
    }
}

const x = f();

console.log(x());

呼び出すxと、 が返されますbbしかし、それはグローバルにはなりません。グローバルにするには、これを行うb必要があります。

function f() {
    const b = "barb";

    return function() {
        return b;
    }
}

const x = f();
const b = x();
console.log(b);

これがスコープと関数についての理解に役立つことを願っています。

于 2013-06-24T17:15:50.907 に答える
2
    function f1() {
          var a = 1;
          f2();
          }

    function f2() {
       return a;
    }
    f1(); // a is not defined
  1. f2(); 「a」を渡したことがないため、aについてはわかりません(これは、関数が定義されたときにスコープが作成されます)。関数f2()は、f1()内で定義されている場合、aにアクセスできたはずです。 [関数は、変数が「DEFINED」で「CALLED」ではない同じスコープ内の変数にアクセスできます]

    function f() {
       var b = "barb";
       return function(){
                         return b;
                        }
    }
    console.log(b); 
    
  2. まず、f(); を呼び出す必要があります。f() を実行した後; 実行する必要がある別の関数を返します。すなわち

    var a=f();
    a();
    

    それは "barb" になります。この場合、変数 b ではなく関数を返しています。

    function f() {
       var b = "barb";
       return b;
                 };
    
    console.log(f());
    

    これにより、バーブが画面に出力されます

于 2014-02-03T14:24:30.087 に答える