関数を作成する標準フォームの概要は次のとおりです(元々は別の質問のために書かれましたが、正規の質問に移動した後に適応されました)
条項:
クイックリスト:
関数宣言
「匿名」function
式(用語にもかかわらず、名前付きの関数を作成する場合があります)
名前付きfunction
式
アクセサ関数初期化子(ES5 +)
アロー関数式(ES2015 +)(無名関数式のように、明示的な名前は含まれていませんが、名前付きの関数を作成できます)
オブジェクト初期化子でのメソッド宣言(ES2015 +)
class
(ES2015 +)のコンストラクターとメソッドの宣言
関数宣言
最初の形式は、次のような関数宣言です。
function x() {
console.log('x');
}
関数宣言は宣言です。ステートメントや式ではありません。したがって、それに続くのは;
(無害ですが)ありません。
関数宣言は、ステップバイステップのコードが実行される前に、実行が出現するコンテキストに入ったときに処理されます。作成される関数には適切な名前が付けられ(x
上記の例では)、その名前は宣言が現れるスコープに置かれます。
同じコンテキストでステップバイステップのコードの前に処理されるため、次のようなことができます:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
ES2015までは、仕様では、あなたのような制御構造内の関数宣言を置けば、JavaScriptエンジンが何をすべきかをカバーしていないtry
、if
、switch
、while
など、このような:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
また、これらはステップバイステップのコードが実行される前に処理されるため、制御構造内にあるときに何をすべきかを知るのは難しいです。
これを行うことはES2015まで指定されていませんでしたが、ブロックでの関数宣言をサポートするための許容可能な拡張でした。残念なことに(そして必然的に)、エンジンが異なれば異なることも行われました。
ES2015の時点で、仕様には何をすべきかが記載されています。実際、次の3つのことを行うことができます。
- Webブラウザー上ではないルーズモードの場合、JavaScriptエンジンは1つのことを行うことになっています
- Webブラウザーでルーズモードの場合、JavaScriptエンジンは他のことを行うことになっています
- した場合、厳密なモード(ブラウザまたはしない)、JavaScriptエンジンは、また別のことを行うことになっています
ルーズモードのルールはトリッキーですが、ストリクトモードでは、ブロックでの関数宣言は簡単です。これらはブロックに対してローカルであり(ES2015でも新しく追加されたブロックスコープを持ち)、先頭に移動しますブロックの。そう:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
「匿名」function
表現
2番目の一般的な形式は、無名関数式と呼ばれます。
var y = function () {
console.log('y');
};
すべての式と同様に、コードの段階的な実行で到達したときに評価されます。
ES5では、これによって作成される関数には名前がありません(匿名)。ES2015では、可能であればコンテキストから推測することで、関数に名前が割り当てられます。上記の例では、名前はになりますy
。関数がプロパティ初期化子の値である場合、同様のことが行われます。(これが発生するタイミングとルールの詳細については、仕様を検索しSetFunctionName
てください。 それは至る所に表示されます。)
名前付きfunction
式
3番目の形式は、名前付き関数式( "NFE")です。
var z = function w() {
console.log('zw')
};
これが作成する関数には適切な名前があります(w
この場合)。すべての式と同様に、これは、コードの段階的な実行で到達したときに評価されます。関数の名前は、式が表示されるスコープには追加されません。名前は関数自体のスコープ内にあります:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
NFEは、JavaScript実装のバグの原因となることが多いことに注意してください。たとえば、IE8以前では、NFEを完全に誤って処理し、2つの異なる時点で2つの異なる関数を作成します。Safariの初期バージョンにも問題がありました。良いニュースは、ブラウザの現在のバージョン(IE9以降、現在のSafari)では、これらの問題がなくなったことです。(しかし、これを書いている時点で残念なことに、IE8は依然として広く使用されているため、Web用のコードでNFEを使用することには依然として問題があります。)
アクセサ関数初期化子(ES5 +)
時々、機能がほとんど気付かれずにこっそり入ることがあります。これは、アクセサ関数の場合です。次に例を示します。
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
この関数を使用したときは使用しなかったことに注意してください()
。これは、プロパティのアクセサ関数であるためです。通常の方法でプロパティを取得および設定しますが、裏で関数が呼び出されます。
、、およびあまり知られていない第2引数を使用してObject.defineProperty
、アクセサ関数を作成することもできます。Object.defineProperties
Object.create
アロー関数式(ES2015 +)
ES2015は、矢印機能をもたらします。以下はその一例です。
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
通話にn => n * 2
隠れていることを確認しmap()
ますか?それは関数です。
アロー関数に関するいくつかのこと:
彼らは自分自身を持っていませんthis
。その代わりに、彼らは近くにわたりthis
、それらが定義されているコンテキストの。(また、それらは近くにarguments
あり、関連する場合)super
。これは、this
それらの内部this
がそれらが作成された場所と同じであり、変更できないことを意味します。
上記でお気づきのように、キーワードは使用しませんfunction
。代わりに、を使用します=>
。
上記のn => n * 2
例はそれらの1つの形式です。関数を渡す引数が複数ある場合は、括弧を使用します。
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Array#map
最初の引数としてエントリを渡し、2番目の引数としてインデックスを渡すことを忘れないでください。)
どちらの場合も、関数の本体は単なる式です。関数の戻り値は自動的にその式の結果になります(明示的なは使用しませんreturn
)。
1つ以上の式を実行している場合は、通常どおり{}
、明示的にreturn
(値を返す必要がある場合)を使用します。
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
なしのバージョン{ ... }
は、式本体または簡潔な本体を持つ矢印関数と呼ばれます。(また:簡潔な機能矢印)を有するもの{ ... }
体を定義が有する矢印関数である関数本体。(また:詳細な矢印関数。)
オブジェクト初期化子でのメソッド宣言(ES2015 +)
ES2015では、メソッド定義と呼ばれる関数を参照するプロパティをより短い形式で宣言できます。次のようになります。
var o = {
foo() {
}
};
ES5以前でほぼ同等のものは次のようになります。
var o = {
foo: function foo() {
}
};
違い(冗長性以外)は、メソッドはを使用できますsuper
が、関数は使用できないことです。したがって、たとえば、valueOf
メソッド構文を使用して(たとえば)定義されたオブジェクトがある場合super.valueOf()
、値を取得するためObject.prototype.valueOf
に(おそらく他の何かを実行する前に)オブジェクトが返されますが、ES5バージョンはObject.prototype.valueOf.call(this)
代わりに行う必要があります。
これは、メソッドがそれが定義されたオブジェクトへの参照を持っていることも意味します。そのため、そのオブジェクトが一時的である場合(たとえば、Object.assign
ソースオブジェクトの1つとしてそれを渡す場合)、メソッド構文はオブジェクトが保持されることを意味します。それ以外の場合はガベージコレクションされた可能性のあるメモリ内(JavaScriptエンジンがその状況を検出せず、どのメソッドもを使用しない場合は処理しますsuper
)。
class
(ES2015 +)のコンストラクターとメソッドの宣言
ES2015はclass
、宣言されたコンストラクターとメソッドを含む構文を提供します。
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
上記の2つの関数宣言があります。1つは名前を取得するコンストラクタPerson
用でgetFullName
、もう1つはに割り当てられた関数Person.prototype
です。