4

したがって、次のコードを使用して、ネストされた関数が実際に外側の関数のパラメーターのコピーを取得することを自分自身に証明しました。

    var o = {};
    (function(a,b,c,x){
        x.f = function(){
            return a.toString()+b.toString()+c.toString();
        }
    })(7,4,2,o);
    o.f();

コードは

    742

これは、o.f関数が無名関数から a、b、および c のコピーを取得することを意味します。そうでなければ、私はただ得るでしょうundefinedundefinedundefined

私の質問は、

  • まず、これは常に真実ですか?または厳しい状況がありますか?(のように、それはオブジェクトである必要がありますか?など)
  • また、javascript には、このような他の種類のあいまいなスコープが存在しますか? 知りたいです(つまり、3回目の反復はどうですか?)
  • 最後に、javascript スコープに関する高度な概念を説明するドキュメントを読んでもまったく問題ありません。誰か良いものを知っていますか?
4

4 に答える 4

11

あなたが観察するものはlexical scopeと呼ばれます。これは、JavaScript の特定のスコープ内の変数のバインディングが、変数がコード内のどこに現れるかによって決定されることを意味します。それは常に真であり、どのレベルでも真です。唯一の主な例外は、字句スコープではなく動的スコープthisの値です。動的スコープとは、関数内の変数が、関数が呼び出される方法とタイミングに依存することを意味します。(字句スコープと動的スコープを参照してください。)

例:

var o = { 
    a: function() {
        var x = 5;
        console.log(this, x);
        function b() {
            console.log(this, x);
        }
        b();
    }
};

o.a();

この例の結果は次のようになります。

{Object o} 5
{window} 5

つまり、1つ目はオブジェクトへの参照としてconsole.logログに記録され、2 つ目はオブジェクトへの参照としてログに記録されます。ただし、どちらもに等しいとログに記録されます。の値は、非厳密モードでコンテキストなしで呼び出された場合です。Soはレキシカルにスコープされていませんが、他の変数は同様です。の動作の詳細については、の概要を参照してください。thisoconsole.logthiswindowx5thiswindowthisxthisthis


質問に直接答えるには:

1. まず、これは常に正しいのですか? または厳しい状況がありますか?(のように、それはオブジェクトである必要がありますか?など)

はい、関数の呼び出し方法に基づいて変化するthisとを除いて、それは当てはまります。argumentsオブジェクトである必要はありません。すべての変数はレキシカル スコープです。


2. また、javascript には、このような不明瞭なスコープが他にどのようなものがありますか? 知りたいです(つまり、3回目の反復はどうですか?)

必要なだけ深くすることができます。内部関数は常に外部関数の変数にアクセスできます。

function a() {
    var x = 1;
    console.log('a:', x);
    return function b() {
        var y = 2;
        console.log('b:', x, y);
        return function c() {
            console.log('c:', x, y);
        };
    };
}

var foo = a();   // => logs 'a: 1'
var bar = foo(); // => logs 'b: 1 2'
bar();           // => logs 'c: 1 2'

これは、実際にはクロージャーと呼ばれる別のトピックの一部であり、別の関数内から関数を返すときに発生します。


3. 最後に、javascript スコープに関する高度な概念を説明するドキュメントを読んでもまったく問題ありません。誰か良いものを知っていますか?

すでにいくつかのリソースにリンクしています。別の良いもの:

MDN: 関数と関数のスコープ(具体的にはNested Functions and Closuresのセクション)。

また、クロージャーに関するものを読むことでメリットが得られます。また、レキシカル スコープを調べることもできます。

于 2012-12-09T06:13:54.770 に答える
1

コードでこの概念を使用しているときにばかげた間違いを犯し、パラメーターを変数として誤って再宣言しました。他の人に役立つ場合に備えて、ここに投稿します。

(function(){
var outerFunction=function(varA){
    console.debug('value of varA in outer function '+varA);
    var innerFunction = function(){
        console.debug('value of varA in inner function '+varA);
    }
    innerFunction();
};
outerFunction("hi");

var buggyOuterFunction=function(varA){
    console.debug('value of varA in buggy outer function '+varA);
    var buggyInnerFunction = function(){
        var varA = varA || "hello"; //BUG - do not redeclare
        console.debug('value of varA in buggy inner function '+varA);
    }
    buggyInnerFunction();
};
buggyOuterFunction("hi");
})();

出力:

value of varA in outer function hi
value of varA in inner function hi
value of varA in buggy outer function hi 
value of varA in buggy inner function hello
于 2013-09-10T06:54:09.540 に答える
1

あなたがする必要があるのは、閉鎖について読むことだけです。http://en.wikipedia.org/wiki/Closure_(computer_science ) 内部関数はその変数のコピーを取得せず、その変数への参照を取得します。例:

var o = {};
(function(a,b,c,x){
    x.f = function(){
        return a.toString()+b.toString()+c.toString();
    }
    a++;
})(7,4,2,o);
o.f();

収量

842

編集: わかりました。読み進める必要があるのはそれだけではないかもしれません。この概念が多くの言語にとって異質なものではないことを示す価値があると思います。

于 2012-12-09T05:57:08.740 に答える
0
  • はい、これはすべてのタイプに当てはまります。JavaScript 設計のこの要素は「クロージャー」と呼ばれます。追加の読み物をグーグルで検索するときにそれを使用してください。
  • 関数を作成できるネストの深さには言語の制限はなく、保守性の制限のみがあります。一般に、第 2 レベルの後、「閉鎖地獄」または「閉鎖スパゲッティ」に陥り、リファクタリングの時期になります。したがって、関数を 4 レベルの深さでネストし、関数 4 内のコードから関数 1 のパラメーターにアクセスできます。
    • 他に 2 つの「あいまいな」ケースが思い浮かびます。
      • this明示的なバインドなしで関数を直接呼び出すthisと、グローバル (ウィンドウ) オブジェクトにバインドされます。これは、javascript の重大な欠陥の 1 つとして広く認識されています。注意してください。
      • JavaScript には「関数巻き上げ」があります。これは、実際の関数定義が同じスコープ内のソース コード ファイルのさらに下にある場合でも、スコープの先頭から関数を参照できることを意味します。
  • Douglas Crockford のJavaScript: The Good Partsを読んでください。また、多くの優れたチュートリアルやオンライン ブックが無料で入手でき、Web 検索で簡単に見つけることができます。それらが最新であることを確認してください(ほとんどの場合、2010年以降に書かれています)。
于 2012-12-09T06:01:20.090 に答える