ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript_javascript スキルにおける関数宣言と関数式の違いの簡単な分析

JavaScript_javascript スキルにおける関数宣言と関数式の違いの簡単な分析

WBOY
WBOYオリジナル
2016-05-16 16:07:261063ブラウズ

私がテンセントでインターンとして面接していたとき、面接官が私にこの質問をしたのを覚えています。

コードをコピー コードは次のとおりです:

//次の 2 つの宣言メソッドの違いは何ですか

関数 foo(){};

var bar = function foo(){};

最初は関数宣言と関数式という 2 つの宣言方法しか知らなかったので、具体的な違いを説明できませんでした。最近たまたまこのテーマに関する本を見つけたので、それについてまとめてみようと思いました。

ECMAScript では、関数オブジェクトを作成するために最も一般的に使用される 2 つの方法があります。それは、関数式を使用する方法と関数宣言を使用する方法です。この点に関して、ECMAScript 仕様では、関数宣言には常に関数名と呼ばれる識別子 (Identifier) が必要であり、関数式は省略できることが明確にされています。

関数宣言:

コードをコピーします コードは次のとおりです:

関数識別子 (FormalParameterList オプション){ FunctionBody }

関数宣言の解析プロセスは次のとおりです:

1. Functionオブジェクトを新規作成し、FormalParameterListでパラメータを指定し、FunctionBodyで関数本体を指定します。現在実行中の環境内のスコープ チェーンをスコープとして使用します。

2. 現在の変数オブジェクトの Identifier という名前の属性を、値 Result(1) で作成します。

関数式:

(関数式は匿名関数式と名前付き関数式に分かれます)

コードをコピー コードは次のとおりです:

function Identifier opt(FormalParameterList opt){ FunctionBody } //ここに名前付き関数式があります

名前付き関数式の解析プロセスは次のとおりです:

1. 新しいオブジェクトを作成します
2. Result(1) をスコープ チェーンの先頭に追加します
3. 新しい Function オブジェクトを作成し、FormalParameterList にパラメータを指定し、FunctionBody に関数本体を指定します。現在実行中の実行環境のスコープ チェーンをスコープとして使用します。
4. Result(1) の Identifier という名前の属性を作成します。その値は Result(3) で、読み取り専用で、削除できません
5. スコープチェーンから Result(1)
を削除します。 6. 結果を返す(3)

公式ドキュメントは非常に読みにくいです。簡単に言えば、ECMAScript はコンテキストを通じて 2 つを区別します。関数 foo(){} が代入式の一部である場合、それは関数式とみなされます。また、関数 foo(){} が関数本体内に含まれているか、プログラム (の最上位) に配置されている場合、関数宣言として解析されます。明らかに、識別子が省略された場合、「式」は式のみになります。

コードをコピーします コードは次のとおりです:

function foo(){}; // プログラムの一部であるための宣言

var bar = function foo(){} // 代入式 (AssignmentExpression) の一部であるため、式

new function bar(){}; // 式。New 式 (NewExpression)
の一部です。
(関数(){
Function bar(){}; // 関数本体 (FunctionBody)
の一部であるための宣言 })();

別の状況もあります:

コードをコピー コードは次のとおりです:

(関数 foo(){})

このケースも関数式であり、そのコンテキストでは、() はグループ化演算子を構成し、グループ化演算子には式のみを含めることができます。

コードをコピー コードは次のとおりです:
function foo(){}; // 関数宣言

(function foo(){}); // 関数式: グループ化演算子
に含まれることに注意してください。
{
を試してください (var x = 5); // グループ化演算子にはステートメントではなく式のみを含めることができます (ここでは var はステートメントです)
}
catch(err) {
// SyntaxError (「var x = 5」は式ではなくステートメントであるため、式の評価は値を返す必要がありますが、ステートメントの評価は必ずしも値を返すとは限りません。 - 翻訳
}

関数宣言と関数式の類似点と相違点について簡単に説明しましょう。宣言と式の動作には、微妙ですが重要な違いがあります。

まず、式が解析されて評価される前に、 関数宣言が解析されて評価されます 。宣言がソース コードの最後の行であっても、同じスコープ内の最初の式より前に評価されます。例を見ると理解しやすいです。次の例では、alert の後に関数 fn が宣言されています。ただし、アラートが実行されるとき、fn はすでに定義されています:

コードをコピー コードは次のとおりです:

alert(fn()); //Helloworld! を出力します。
関数 fn() {
return 'Helloworld!';
}

簡単にまとめると、何が違うのでしょうか?

1. 宣言は常にスコープの先頭で解析されます。 2. 式は、見つかった場合にのみ評価されます。


関数宣言にはもう 1 つの重要な特徴があります。

つまり、条件文を使用して関数宣言を制御する動作が標準化されていないため、環境が異なれば異なる結果が得られる可能性があります

。つまり:

コードをコピーします コードは次のとおりです:
// 絶対にやってはいけない!
// ブラウザーが異なれば、返される結果も異なります。

if (true) {
関数 foo() {
「最初」を返します;
}
}
他 {
関数 foo() {
'秒' を返します;
}
}
foo();


// この場合、忘れずに関数式を使用してください:
var foo;
if (true) {
foo = function() {
「最初」を返します;
};
}
他 {
foo = function() {
'秒' を返します;
};
}
foo();


それでは、関数宣言を使用するための実際のルールは何でしょうか?

FunctionDeclaration は、Program または FunctionBody 内でのみ使用できます。構文的には、ブロック ({ ... }) 内 (たとえば、if、while、または for ステートメント内) にこれらを使用することはできません。 Block には Statement のみを含めることができ、FunctionDeclaration などの SourceElement は含めることができないためです。

一方、生成ルールを詳しく見てみると、式をブロック内に出現させる唯一の方法は、それを ExpressionStatement の一部にすることであることがわかります。ただし、仕様では、ExpressionStatement をキーワード関数で開始できないと明確に述べられています。これが実際に意味するのは、FunctionExpression を Statement または Block 内に使用できないということです (Block は Statement で構成されているということを忘れないでください)。

上記の制限により、関数がブロック内に出現するときは常に (上の例のように)、関数の宣言や式ではなく、実際には構文エラーとして扱われる必要があります。

では、関数宣言や関数式はどのようなときに使用すればよいのでしょうか?関数宣言は「プログラム コード」内でのみ出現できます。つまり、関数宣言は、他の関数本体またはグローバル空間内でのみ出現できます。その定義は、変数や属性に割り当てたり、関数呼び出しのパラメーターとして渡すことはできません。例としては、関数宣言の使用が許可されており、foo()、bar()、および local() はすべて関数宣言モードで宣言されています:

コードをコピーします コードは次のとおりです:
//地球環境
関数 foo() {}

関数 local() {
// ローカル環境
関数 bar() {}
リターンバー
}


構文的に関数宣言を使用できない場合は、関数式を使用できます。例: 関数をパラメータとして渡すか、オブジェクト リテラルで関数を定義します:


コードをコピー コードは次のとおりです:

// これは匿名関数式です
callMe(function() {

//関数をパラメータとして渡します
});

// これは名前付き関数式です
callMe(function me() {

// 関数をパラメータとして渡します。関数名は me
です。 });

// その他の関数式
var myobject = {
言う: function () {

// 私は関数式です
}
};

私の知識には限界があるので、間違いがあれば修正してください。

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