関数を作成する標準フォームの概要は次のとおりです。
条項:
クイック リスト:
関数宣言
「匿名」function
式(この用語にもかかわらず、名前付きの関数を作成することがあります)
名前付きfunction
式
アクセサー関数初期化子 (ES5+)
アロー関数式 (ES2015+) (無名関数式と同様に、明示的な名前を必要とせず、名前付きの関数を作成できます)
オブジェクト初期化子でのメソッド宣言 (ES2015+)
class
(ES2015+)のコンストラクターとメソッドの宣言
関数宣言
最初の形式は関数宣言で、次のようになります。
function x() {
console.log('x');
}
関数宣言は宣言です。それは声明や表現ではありません。そのため、a を続けません;
(ただし、無害です)。
関数宣言は、ステップバイステップ コードが実行される前に、実行がそれが表示されるコンテキストに入ったときに処理されます。それが作成する関数には適切な名前が付けられ (x
上の例では)、その名前は宣言が表示されるスコープに入れられます。
これは、同じコンテキスト内のステップバイステップ コードの前に処理されるため、次のようなことができます。
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
try
ES2015 まで、次のようにif
、switch
、while
、 などの制御構造内に関数宣言を配置した場合に JavaScript エンジンが何をすべきかについて、仕様はカバーしていませんでした。
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
また、それらは段階的なコードが実行される前に処理されるため、制御構造内にあるときに何をすべきかを理解するのは困難です。
これを行うことはES2015 まで指定されていませんでしたが、ブロック内の関数宣言をサポートするために許可された拡張機能でした。残念なことに (そして必然的に)、エンジンが異なれば動作も異なります。
ES2015 の時点で、仕様は何をすべきかを示しています。実際、それは3つの別々のことをすることを与えます:
- Web ブラウザーではないルース モードの場合、JavaScript エンジンは 1 つのことを行うことになっています。
- Web ブラウザーでルース モードの場合、JavaScript エンジンは別の処理を行うことになっています。
- 厳密モード (ブラウザーかどうかに関係なく) の場合、JavaScript エンジンはさらに別のことを行うことになっています。
Loose モードのルールはトリッキーですが、strictモードでは、ブロック内の関数宣言は簡単です: それらはブロックに対してローカルであり (ブロック スコープがあり、これも 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"
()
関数を使用したときに、 !を使用しなかったことに注意してください。これは、プロパティのアクセサー関数であるためです。通常の方法でプロパティを取得および設定しますが、裏で関数が呼び出されます。
Object.defineProperty
、Object.defineProperties
、およびあまり知られていない の 2 番目の引数を使用してアクセサ関数を作成することもできます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
)。
複数の式を実行している場合は、通常どおり{}
and を明示的に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
もう 1 つはgetFullName
に割り当てられた関数である用Person.prototype
です。