関数は呼び出すことができる値です。関数を定義する1つの方法は、関数宣言と呼ばれます。 例えば、次のコードは、単一のパラメータxを持つ関数idを定義しています。
functionid(x){returnx;}
return文は、idから値を返します。 関数は、その名前を括弧で囲んだ引数を続けて記述することで呼び出すことができます。
> id('hello')
'hello'関数から何も返さない場合、undefinedが(暗黙的に)返されます。
> function f() { }
> f()
undefinedこのセクションでは、関数を定義する1つの方法と呼び出す1つの方法のみを示しました。他の方法は後述します。
関数を上記のように定義すると、いくつかの役割を果たすことができます。
関数を直接呼び出すことができます。 その場合、通常の関数として機能します。呼び出しの例を次に示します。
id('hello')
慣例により、通常の関数の名前は小文字で始まります。
new演算子を使用して関数を呼び出すことができます。 その場合、オブジェクトのファクトリであるコンストラクタになります。呼び出しの例を次に示します。
newDate()
慣例により、コンストラクタの名前は大文字で始まります。
関数をオブジェクトのプロパティに格納することができます。これにより、そのオブジェクトを介して呼び出すことができるメソッドになります。 呼び出しの例を次に示します。
obj.method()
慣例により、メソッドの名前は小文字で始まります。
非メソッド関数は本章で説明します。コンストラクタとメソッドは第17章で説明します。
用語パラメータと引数は、多くの場合、同じ意味で使用されます。これは、コンテキストによって意図する意味が通常は明確になるためです。それらを区別するための経験則を以下に示します。
パラメータは関数を定義するために使用されます。 これらは、仮パラメータおよび仮引数とも呼ばれます。次の例では、param1とparam2はパラメータです。
functionfoo(param1,param2){...}
引数は関数を呼び出すために使用されます。 これらは、実パラメータおよび実引数とも呼ばれます。次の例では、3と7は引数です。
foo(3,7);
このセクションでは、関数を定義する3つの方法について説明します。
Function()による方法すべての関数はオブジェクトであり、Functionのインスタンスです。
functionid(x){returnx;}console.log(idinstanceofFunction);// true
したがって、関数はFunction.prototypeからメソッドを取得します。
関数式は値(関数オブジェクト)を生成します。次に例を示します。
varadd=function(x,y){returnx+y};console.log(add(2,3));// 5
上記のコードは、関数式の結果を変数addに代入し、その変数を介して呼び出しました。関数式によって生成される値は、変数に代入したり(最後の例で示したように)、別の関数の引数として渡したりすることができます。通常の関数式には名前がないため、無名関数式とも呼ばれます。
関数式に名前を付けることができます。 名前付き関数式を使用すると、関数式が自身を参照できるようになります。これは、自己再帰に役立ちます。
varfac=functionme(n){if(n>0){returnn*me(n-1);}else{return1;}};console.log(fac(3));// 6
名前付き関数式の名前は、関数式内でのみアクセスできます。
varrepeat=functionme(n,str){returnn>0?str+me(n-1,str):'';};console.log(repeat(3,'Yeah'));// YeahYeahYeahconsole.log(me);// ReferenceError: me is not defined
以下は関数宣言です。
functionadd(x,y){returnx+y;}
上記は関数式のように見えますが、ステートメントです(式とステートメントを参照)。これは、おおよそ次のコードと同等です。
varadd=function(x,y){returnx+y;};
つまり、関数宣言は新しい変数を宣言し、関数オブジェクトを作成し、それを変数に代入します。
コンストラクタFunction()は、文字列に格納されているJavaScriptコードを評価します。 例えば、次のコードは前の例と同等です。
varadd=newFunction('x','y','return x + y');
ただし、この関数の定義方法は遅く、コードを文字列に保持します(ツールからアクセスできません)。したがって、可能な場合は、関数式または関数宣言を使用する方がはるかに優れています。 new Function()を使用したコードの評価では、Function()について詳しく説明しています。これは、eval()と同様に機能します。
ホイストとは、「スコープの先頭に移動する」ことを意味します。 関数宣言は完全にホイストされますが、変数宣言は部分的にのみホイストされます。
関数宣言は完全にホイストされます。これにより、宣言される前に関数を呼び出すことができます。
foo();functionfoo(){// this function is hoisted...}
上記のコードが機能する理由は、JavaScriptエンジンがfooの宣言をスコープの先頭に移動するためです。まるで次のようになっているかのようにコードを実行します。
functionfoo(){...}foo();
var宣言もホイストされますが、宣言のみがホイストされ、それらを使用して行われた代入はホイストされません。 したがって、前の例と同様にvar宣言と関数式を使用すると、エラーが発生します。
foo();// TypeError: undefined is not a functionvarfoo=function(){...};
変数宣言のみがホイストされます。エンジンは上記のコードを次のように実行します。
varfoo;foo();// TypeError: undefined is not a functionfoo=function(){...};
ほとんどのJavaScriptエンジンは、関数オブジェクトの非標準プロパティnameをサポートしています。 関数宣言には名前があります。
> function f1() {}
> f1.name
'f1'無名関数式の名前は空の文字列です。
> var f2 = function () {};
> f2.name
''ただし、名前付き関数式には名前があります。
> var f3 = function myName() {};
> f3.name
'myName'関数の名前はデバッグに役立ちます。そのため、常に関数式に名前を付ける人もいます。
次のような関数宣言を優先する必要がありますか?
functionid(x){returnx;}
または、var宣言と関数式の同等の組み合わせですか?
varid=function(x){returnx;};
基本的には同じですが、関数宣言には関数式よりも2つの利点があります。
call()、apply()、およびbind()は、すべての関数が持つメソッドです(関数はオブジェクトであるため、メソッドがあることを忘れないでください)。 これらは、メソッドを呼び出すときにthisの値を指定できるため、主にオブジェクト指向のコンテキストで興味深いものです(thisを設定しながら関数を呼び出す:call()、apply()、およびbind()を参照)。このセクションでは、非メソッドの2つのユースケースについて説明します。
このメソッドは、関数funcを呼び出すときにargArrayの要素を引数として使用します。つまり、次の2つの式は同等です。
func(arg1,arg2,arg3)func.apply(null,[arg1,arg2,arg3])
thisValueは、funcの実行中にthisが持つ値です。オブジェクト指向ではない設定では必要ないため、ここではnullです。
apply()は、関数が配列のような方法で複数の引数を受け入れるが、配列を受け入れない場合に役立ちます。
apply()のおかげで、Math.max()(その他の関数を参照)を使用して、配列の最大要素を決定できます。
> Math.max(17, 33, 2) 33 > Math.max.apply(null, [17, 33, 2]) 33
これは部分関数適用を実行します。 thisをthisValueに設定し、次の引数:最初にarg1からargNまで、次に新しい関数の実際の引数を使用してfuncを呼び出す新しい関数が作成されます。thisValueは、以下のオブジェクト指向ではない設定では必要ないため、nullです。
ここでは、bind()を使用して、add()に似ているが、パラメータyのみを必要とする新しい関数plus1()を作成します。これは、xが常に1であるためです。
functionadd(x,y){returnx+y;}varplus1=add.bind(null,1);console.log(plus1(5));// 6
言い換えれば、次のコードと同等の新しい関数を作成しました。
functionplus1(y){returnadd(1,y);}
JavaScriptは関数のarityを強制しません。定義されている仮パラメータに関係なく、任意の数の実際のパラメータを使用して関数を呼び出すことができます。したがって、実際のパラメータと仮パラメータの数は、次の2つの方法で異なる可能性があります。
arguments(すぐに説明します)を介して取得できます。undefinedを持ちます。特殊変数argumentsは、関数内(メソッドを含む)でのみ存在します。これは、現在の関数呼び出しのすべての実パラメータを保持する配列のようなオブジェクトです。次のコードはそれを使用しています。
functionlogArgs(){for(vari=0;i<arguments.length;i++){console.log(i+'. '+arguments[i]);}}
そして、これが相互作用です。
> logArgs('hello', 'world')
0. hello
1. worldargumentsには次の特徴があります。
配列に似ていますが、配列ではありません。 一方では、プロパティlengthがあり、個々のパラメータはインデックスによって読み書きできます。
一方、argumentsは配列ではなく、配列に似ているだけです。配列メソッド(slice()、forEach()など)はありません。配列のようなオブジェクトとジェネリックメソッドで説明されているように、ありがたいことに、配列メソッドを借用したり、argumentsを配列に変換したりできます。
オブジェクトなので、すべてのオブジェクトメソッドと演算子を使用できます。 例えば、in演算子(プロパティの反復と検出)を使用して、argumentsに特定のインデックスが「ある」かどうかを確認できます。
> function f() { return 1 in arguments }
> f('a')
false
> f('a', 'b')
true同様の方法でhasOwnProperty()(プロパティの反復と検出)を使用できます。
> function g() { return arguments.hasOwnProperty(1) }
> g('a', 'b')
truestrict モードでは、`arguments` のいくつかの特殊な機能が削除されます。
arguments.callee は現在の関数を参照します。これは主に無名関数で自己再帰を行うために使用されますが、strict モードでは許可されていません。回避策として、名前付き関数式(名前付き関数式を参照)を使用してください。名前付き関数式は、その名前を介して自身を参照できます。非strictモードでは、パラメータを変更すると arguments は最新の状態に保たれます。
functionsloppyFunc(param){param='changed';returnarguments[0];}console.log(sloppyFunc('value'));// changed
しかし、strict モードではこの種の更新は行われません。
functionstrictFunc(param){'use strict';param='changed';returnarguments[0];}console.log(strictFunc('value'));// value
arguments への代入(例:arguments++ による)は禁止されています。要素とプロパティへの代入は引き続き許可されています。パラメータが欠落しているかどうかを調べるには、3つの方法があります。 まず、それが undefined かどうかを確認できます。
functionfoo(mandatory,optional){if(mandatory===undefined){thrownewError('Missing parameter: mandatory');}}
次に、パラメータをブール値として解釈できます。この場合、undefined は false と見なされます。ただし、注意点があります。他のいくつかの値も false と見なされるため(Truthy と Falsy の値を参照)、このチェックでは、たとえば 0 と欠落しているパラメータを区別できません。
if(!mandatory){thrownewError('Missing parameter: mandatory');}
3つ目に、arguments の長さをチェックして、最小引数を強制することもできます。
if(arguments.length<1){thrownewError('You need to provide at least 1 argument');}
最後のアプローチは他のアプローチとは異なります。
foo() と foo(undefined) を区別しません。どちらの場合も、例外がスローされます。foo() では例外をスローし、foo(undefined) では optional を undefined に設定します。パラメータがオプションの場合、欠落している場合はデフォルト値を指定することを意味します。 必須パラメータと同様に、4つの選択肢があります。
まず、undefined かどうかを確認します。
functionbar(arg1,arg2,optional){if(optional===undefined){optional='default value';}}
次に、optional をブール値として解釈します。
if(!optional){optional='default value';}
3つ目に、論理和演算子 ||(論理和 (||)を参照)を使用できます。これは、左側のオペランドがfalsyでない場合はそれを返します。そうでない場合は、右側のオペランドを返します。
// Or operator: use left operand if it isn't falsyoptional=optional||'default value';
4つ目に、arguments.length を介して関数の引数の数をチェックできます。
if(arguments.length<3){optional='default value';}
ここでも、最後のアプローチは他のアプローチとは異なります。
bar(1, 2) と bar(1, 2, undefined) を区別しません。どちらの場合も、optional は 'default value' です。bar(1, 2) では optional を 'default value' に設定し、bar(1, 2, undefined) では undefined(つまり、変更なし)のままにします。もう1つの可能性は、オプションパラメータを名前付きパラメータとして、オブジェクトリテラルのプロパティとして渡すことです(名前付きパラメータを参照)。
JavaScriptでは、パラメータを参照渡しすることはできません。つまり、変数を関数に渡すと、その値がコピーされて関数に渡されます(値渡し)。そのため、関数は変数を変更できません。変更する必要がある場合は、変数の値を(たとえば、配列に)ラップする必要があります。
この例は、変数をインクリメントする関数を示しています。
functionincRef(numberRef){numberRef[0]++;}varn=[7];incRef(n);console.log(n[0]);// 8
関数 c をパラメータとして別の関数 f に渡す場合、2つのシグネチャに注意する必要があります。
f がパラメータに期待するシグネチャ。f は複数のパラメータを提供する可能性があり、c はそれらのうちいくつ(もしあれば)を使用するかを決定できます。c の実際のシグネチャ。たとえば、オプションパラメータをサポートしている可能性があります。2つが異なる場合、予期しない結果が生じる可能性があります。c は、あなたが知らないオプションパラメータを持っており、f によって提供される追加の引数を誤って解釈する可能性があります。
例として、配列メソッド map()(変換メソッドを参照)を考えてみましょう。そのパラメータは通常、単一のパラメータを持つ関数です。
> [ 1, 2, 3 ].map(function (x) { return x * x })
[ 1, 4, 9 ]引数として渡すことができる関数の1つは、parseInt() です(parseInt() による整数を参照)。
> parseInt('1024')
1024map() は単一の引数のみを提供し、parseInt() は単一の引数のみを受け入れると(誤って)考えるかもしれません。すると、次の結果に驚くでしょう。
> [ '1', '2', '3' ].map(parseInt) [ 1, NaN, NaN ]
map() は、次のシグネチャを持つ関数を期待します。
function(element,index,array)
しかし、parseInt() は次のシグネチャを持っています。
parseInt(string,radix?)
したがって、map() は string(element 経由)だけでなく、radix(index 経由)も入力します。つまり、前の配列の値は次のように生成されます。
> parseInt('1', 0)
1
> parseInt('2', 1)
NaN
> parseInt('3', 2)
NaN要約すると、シグネチャがわからない関数やメソッドには注意してください。それらを使用する場合、どのパラメータが受信され、どのパラメータが渡されるかを明示することが理にかなっていることがよくあります。これは、コールバックによって実現されます。
> ['1', '2', '3'].map(function (x) { return parseInt(x, 10) })
[ 1, 2, 3 ]プログラミング言語で関数(またはメソッド)を呼び出す場合、実パラメータ(呼び出し元によって指定される)を仮パラメータ(関数定義の)にマッピングする必要があります。これを行うには、2つの一般的な方法があります。
名前付きパラメータには、2つの主な利点があります。関数呼び出しの引数の説明を提供することと、オプションパラメータでうまく機能することです。まず利点について説明し、次にオブジェクトリテラルを介してJavaScriptで名前付きパラメータをシミュレートする方法を示します。
selectEntries(3,20,2);
これらの3つの数字は何を意味するのでしょうか? Pythonは名前付きパラメータをサポートしており、何が起こっているのかを簡単に理解できます。
selectEntries(start=3,end=20,step=2)# Python syntax
オプションの位置パラメータは、最後に省略された場合にのみうまく機能します。他の場所では、残りのパラメータが正しい位置になるように、null などのプレースホルダーを挿入する必要があります。オプションの名前付きパラメータを使用すると、それは問題になりません。それらのいずれかを簡単に省略できます。次に例を示します。
# Python syntaxselectEntries(step=2)selectEntries(end=20,start=3)selectEntries()
JavaScript は、Python や他の多くの言語のように、名前付きパラメータをネイティブでサポートしていません。しかし、かなり洗練されたシミュレーションがあります。単一の実パラメータとして渡されるオブジェクトリテラルを介してパラメータに名前を付けます。この手法を使用すると、selectEntries() の呼び出しは次のようになります。
selectEntries({start:3,end:20,step:2});
関数は、プロパティ start、end、および step を持つオブジェクトを受け取ります。それらのいずれかを省略できます。
selectEntries({step:2});selectEntries({end:20,start:3});selectEntries();
selectEntries() は次のように実装できます。
functionselectEntries(options){options=options||{};varstart=options.start||0;varend=options.end||getDbLength();varstep=options.step||1;...}
位置パラメータと名前付きパラメータを組み合わせることもできます。後者を最後に配置するのが慣例です。
someFunc(posArg1,posArg2,{namedArg1:7,namedArg2:true});