5

セクション 13または ECMAScript 仕様 (v. 5)を見ています。無名関数式は次のように初期化されます。

FormalParameterListopt で指定されたパラメーターと FunctionBody で指定された本体を使用して、13.2 で指定された新しい Function オブジェクトを作成した結果を返します。実行中の実行コンテキストの LexicalEnvironment を Scope として渡します。FunctionExpression が厳密なコードに含まれている場合、またはその FunctionBody が厳密なコードである場合は、true を Strict フラグとして渡します。

このロジックは、関数宣言が初期化される方法と非常によく似ています。ただし、名前付き関数式の初期化がどのように異なるかに注意してください。

  1. funcEnv を、実行中の実行コンテキストのレキシカル環境を引数として渡して NewDeclarativeEnvironment を呼び出した結果とします。
  2. envRec を funcEnv の環境レコードとします。
  3. Identifier の String 値を引数として渡して、envRec の CreateImmutableBinding 具象メソッドを呼び出します。
  4. FormalParameterListopt で指定されたパラメーターと FunctionBody で指定された本体を使用して、13.2 で指定されているように新しい Function オブジェクトを作成した結果をクロージャーとします。Scope として funcEnv を渡します。FunctionExpression が厳密なコードに含まれている場合、またはその FunctionBody が厳密なコードである場合は、true を Strict フラグとして渡します。
  5. envRec の InitializeImmutableBinding 具象メソッドを呼び出して、Identifier の String 値とクロージャを引数として渡します。
  6. 閉鎖を返します。

名前付き関数式と無名関数式の大きな違いの 1 つは、名前付き関数式を関数内から再帰的に呼び出すことができることですが、私が考えることができるのはそれだけです。セットアップがこれほど異なるのはなぜですか? また、これらの追加の手順を実行する必要があるのはなぜですか?

4

2 に答える 2

9

その「踊る」理由は単純です。

名前付き関数式の識別子は、関数スコープ内で使用できるようにする必要がありますが、外部では使用できないようにする必要があります。

typeof f; // undefined

(function f() {
  typeof f; // function
})();

f関数内でどのように利用できるようにしますか?

外部のレキシカル環境でバインディングを作成することはできません。これは、外部では使用できないfためです。そして、まだ作成されていないため、内側の変数環境でバインディングを作成することはできません。関数はインスタンス化の時点ではまだ実行されていないため、NewDeclarativeEnvironment を使用した 10.4.3 (関数コードの入力) ステップは発生していません。

したがって、これを行う方法は、現在のものから直接「継承」する中間レキシカル環境を作成し、[[Scope]] として新しく作成された関数に渡すことです。

13 の手順を疑似コードに分割すると、これが明確にわかります。

// create new binding layer
funcEnv = NewDeclarativeEnvironment(current Lexical Environment)

envRec = funcEnv
// give it function's identifier
envRec.CreateImmutableBinding(Identifier)

// create function with this intermediate binding layer
closure = CreateNewFunction(funcEnv)

// assign newly created function to an identifier within this intermediate binding layer
envRec.InitializeImmutableBinding(Identifier, closure)

したがって、内部のレキシカル環境f(たとえば、識別子を解決する場合) は次のようになります。

(function f(){

  [global environment] <- [f: function(){}] <- [Current Variable Environment]

})();

匿名関数を使用すると、次のようになります。

(function() {

  [global environment] <- [Current Variable Environment]

})();
于 2013-03-01T03:26:22.307 に答える
1

スコープに関する2つの取引の主な違い(ただし、他に何もないとしても、そうすることに実際にどれだけ関与しているかを見るのは興味深いです;)-そして、名前付き/匿名関数式の主な違いを指摘することはすでに正しいです名前付きのものを再帰的に呼び出すのが簡単。

なぜ簡単と言うのですか?まあ、実際には無名関数を再帰的に呼び出すことを妨げるものは何もありませんが、それは単にきれいではありません:

//silly factorial, 5!
(function(n) {
  if (n<=1) return 1;
  return (n*arguments.callee(n-1)); //arguments.callee is so 1990s!
})(5);

実際、それは MDN が名前付き関数式の説明で言っていることとまったく同じです!

関数本体内の現在の関数を参照する場合は、名前付き関数式を作成する必要があります。この名前は、関数本体 (スコープ) に対してのみローカルです。これにより、非標準の arguments.callee プロパティの使用も回避されます。

于 2013-02-28T07:42:44.550 に答える