--- title: 関数 slug: Web/JavaScript/Reference/Functions tags: - Function - Functions - Guide - JavaScript - Parameter - parameters translation_of: Web/JavaScript/Reference/Functions ---
一般的に言うと、関数とは外部 (再帰の場合は内部) から 呼ばれる ことのできる「サブプログラム」です。プログラムそのもののように、関数は{{ 訳語("関数本体", "function body") }}と呼ばれる連続した文で構成されます。値を関数に 渡す 事ができ、関数は値を返す事ができます。
JavaScript において、関数は第一級オブジェクトです。すなわち、関数はオブジェクトであり、他のあらゆるオブジェクトと同じように操作したり渡したりする事ができます。具体的には、関数は Function
オブジェクトです。
より詳細な例や解説については、JavaScript の関数のガイドを参照してください。
JavaScript における全ての関数は、実際には Function
オブジェクトです。Function
オブジェクトのプロパティとメソッドについての情報は {{jsxref("Function")}} をご覧ください。
初期値以外の値を返すためには、返す値を指定する return
文が関数内になくてはなりません。return
文を持たない関数は初期値を返します。new
キーワードとともに constructor が呼び出された場合、その this
パラメータが初期値となります。それ以外の全ての関数がデフォルトで返す値は {{jsxref("undefined")}} です。
関数の仮引数 (パラメータ) には、関数呼び出しにおいて実引数 (アーギュメント) が渡されます。実引数は、関数に「値渡し」されます: 関数の中で引数の値を変更しても、その変更はグローバルスコープもしくは呼び出し元の関数内には反映されません。オブジェクト参照も「値」ですが、こちらは特別です: 参照されているオブジェクトのプロパティを関数の中で変更すると、次の例にあるように、その変更を関数の外部から見ることができます:
/* 関数 'myFunc' を宣言 */ function myFunc(theObject) { theObject.brand = "Toyota"; } /* * 変数 'mycar' を宣言 * 新しいオブジェクトの生成と初期化 * 'mycar' への参照をオブジェクトに代入 */ var mycar = { brand: "Honda", model: "Accord", year: 1998 }; /* 'Honda' を出力 */ console.log(mycar.brand); /* オブジェクト参照を関数に渡す */ myFunc(mycar); /* * オブジェクトの 'brand' プロパティの値は関数によって変更されたので * 'Toyota' と出力される */ console.log(mycar.brand);
this
キーワードは現在実行中の関数を参照しません。よって、関数内部であっても、名前によって Function
オブジェクトを参照しなければなりません。
関数を定義するのにはいくつかの方法があります。
function
文)関数を宣言するための特殊な構文があります。(詳細は function 文を参照)
function name([param[, param[, ... param]]]) { statements }
name
param
statements
function
演算子)関数式は、関数宣言と似ており、同じ構文を持っています (詳細は function 演算子を参照)。関数式はより大きな式の一部になることもあります。「名前付き」の関数式を定義することもできます(例えばその名前はコールスタック内で使われるかもしれません)し、「無名の」関数式を定義することもできます。関数式はスコープの開始時に「巻き上げ」られないので、コード内でそれらが登場するより前に使用することはできません。
function [name]([param[, param[, ... param]]]) { statements }
name
param
statements
以下は無名の関数式(名前が使われていない)の例です。
var myFunction = function() { statements }
名前付きの関数式を作るため、定義の中で名前を提供することも可能です。
var myFunction = function namedFunction(){ statements }
名前付きの関数式を作ることのメリットの 1 つは、エラーに遭遇したとき、スタックトレースがその関数の名前を含めるため、エラーの発生源をより容易に特定できるということです。
ここまで見てきたように、どちらの例も function
キーワードから開始されていません。function
から開始せずに関数を含んでいる文が関数式です。
関数を一度だけ使うときの一般的なパターンが {{glossary("IIFE", "IIFE (Immediately Invokable Function Expression)")}} です。
(function() { statements })();
即時関数は、関数を宣言した直後に実行する関数式です。
function*
文)ジェネレーター関数の宣言のための特別な構文です(詳細は {{jsxref('Statements/function*', 'function* 文')}} を参照してください)。
function* name([param[, param[, ... param]]]) { statements }
name
param
statements
function*
演算子)ジェネレーター関数式は、ジェネレーター関数宣言と似ており、同じ構文を持っています (詳細は {{jsxref('Operators/function*', 'function* 演算子')}} を参照してください)。
function* [name]([param[, param[, ... param]]]) { statements }
name
param
statements
アロー関数式は短縮構文を持ち、また関数の this 値を語彙的に束縛します (詳細はアロー関数を参照):
([param[, param]]) => { statements } param => expression
param
()
で示すことが必要です。引数が 1 個の場合のみ、丸括弧は必須ではありません。(例えば foo => 1
)statements または expression
Function
コンストラクタメモ: Function
コンストラクターによる関数の生成は推奨されません。これは、文字列として関数本体が必要で、JS エンジンによる最適化を妨げたり、他の問題を引き起こしたりする場合があるためです。
他の全てのオブジェクトと同じように、new
演算子を使って {{jsxref("Function")}} オブジェクトを作成する事ができます。
new Function (arg1, arg2, ... argN, functionBody)
arg1, arg2, ... argN
functionBody
Function
コンストラクタを関数として (new
演算子を使わずに) 呼び出しても、コンストラクタとして呼び出すのと同じ効果があります。
GeneratorFunction
コンストラクタメモ: GeneratorFunction
はグローバルオブジェクトではありませんが、ジェネレーター関数のインスタンスから得ることができます(詳細は {{jsxref("GeneratorFunction")}} を参照してください)。
メモ: GeneratorFunction
コンストラクタによる関数の生成は推奨されません。これは、文字列として関数本体が必要で、JS エンジンによる最適化を妨げたり、他の問題を引き起こしたりする場合があるためです。
他の全てのオブジェクトと同じように、new
演算子を使って {{jsxref("GeneratorFunction")}} オブジェクトを作成する事ができます。
new GeneratorFunction (arg1, arg2, ... argN, functionBody)
arg1, arg2, ... argN
x
" 、"theValue
"、もしくは "a,b
" などです。functionBody
Function
コンストラクタを関数として (new
演算子を使わずに) 呼び出しても、コンストラクタとして呼び出すのと同じ効果があります。
関数のデフォルト引数は、関数に値が渡されない場合や undefined
が渡される場合に、デフォルト値で初期化される形式上の引数を指定できます。詳細はデフォルト引数を参照してください。
rest parameters とは、不特定多数の引数を配列として受け取る構文です。詳細は rest parameters を参照してください。
arguments
オブジェクトarguments
オブジェクトを使って、関数内部で関数の引数を参照することができます。arguments を参照してください。
arguments
: 現在実行中の関数に渡された引数を格納する配列状オブジェクト。arguments.callee
: 現在実行中の関数。arguments.caller
: 現在実行中の関数を実行した関数。arguments.length
: 関数に渡された引数の数。新しいプロパティの追加をサポートする、どの標準ビルトインオブジェクトあるいはユーザー定義オブジェクトにも、getter(accessor メソッド)や setter (mutator メソッド)を定義することができます。getter と setter を定義するための構文は、オブジェクトリテラル構文を使用します。
オブジェクトのプロパティを、そのプロパティが検索されたときに呼び出される関数に束縛します。
ECMAScript 2015 からは、独自のメソッドを、getter と setter に似た、より短い構文で定義することができます。詳細はメソッド定義を参照してください。
var obj = { foo() {}, bar() {} };
以下のものを比較してみて下さい。
Function
コンストラクタによって定義され、変数 multiply
に代入された関数:
var multiply = new Function('x', 'y', 'return x * y');
multiply
と命名された関数の 関数宣言:
function multiply(x, y) { return x * y; } // ここにセミコロンは必要ありません
変数 multiply
に代入された、無名関数の関数式:
var multiply = function(x, y) { return x * y; };
変数 multiply
に代入された、func_name
と命名された関数式:
var multiply = function func_name(x, y) { return x * y; };
これらは全ておおよそ同じ働きをしますが、いくつか微妙に異なる点があります。
関数名と関数が代入された変数の間には違いがあります。関数名は変える事ができませんが、関数が代入された変数は再代入する事ができます。関数名は関数本体の内部でのみ使用する事ができます。関数本体の外側でそれを使用しようとするとエラー (その関数名がそれより前に var
文によって宣言されていれば undefined
) になります。例えば、
var y = function x() {}; alert(x); // エラーを投げる
関数名は Function
の toString メソッドによってシリアライズしたときにも現れます。
一方、関数が代入された変数はそのスコープ内でのみ有効で、そのスコープは関数が宣言されたスコープを含んでいる事が保証されています。
4 つめの例にあるように、関数名はその関数が代入される変数と違っていても構いません。お互いの間に関連性は有りません。関数宣言は同時にその関数名と同じ名前の変数を作成します。よって、関数式で定義されたものと違って、関数宣言で定義された関数は定義されたスコープ内でその名前によってアクセスできます。
new Function
によって定義された関数は関数名を持ちません。しかし、JavaScript エンジンの SpiderMonkey では、その関数をシリアライズされた形式にすると "anonymous" という名前を持っているかのように表示されます。例えば、alert(new Function())
はこのように出力されます。
function anonymous() { }
この関数は実際には名前を持っていないので、anonymous
は関数内部でアクセスできる変数ではありません。例えば、次の文はエラーになります。
var foo = new Function("alert(anonymous);"); foo();
関数式や Function
コンストラクタで定義されたものとは違い、関数宣言で定義された関数は、関数自体が宣言される前に使用する事ができます。例えば、
foo(); // FOO! とアラートされる function foo() { alert('FOO!'); }
関数式で定義された関数は現在のスコープを継承します。つまり、関数がクロージャを形成します。一方、Function
コンストラクタで定義された関数は (あらゆる関数が継承する) グローバルスコープ以外はどんなスコープも継承しません。
/* * Declare and initialize a variable 'p' (global) * and a function 'myFunc' (to change the scope) inside which * declare a varible with same name 'p' (current) and * define three functions using three different ways:- * 1. function declaration * 2. function expression * 3. function constructor * each of which will log 'p' */ var p = 5; function myFunc() { var p = 9; function decl() { console.log(p); } var expr = function() { console.log(p); }; var cons = new Function('\tconsole.log(p);'); decl(); expr(); cons(); } myFunc(); /* * Logs:- * 9 - for 'decl' by function declaration (current scope) * 9 - for 'expr' by function expression (current scope) * 5 - for 'cons' by Function constructor (global scope) */
関数式と関数宣言で定義された関数は一度しか解析されませんが、Function
コンストラクタで定義された関数はそうではありません。つまり、Function
コンストラクタに渡された関数本体を表す文字列が、評価されるたびに必ず解析されます。関数式は毎回クロージャを作成しますが、関数本体は再解析されないので、"new Function(...)
" よりは関数式の方がまだ高速です。したがって Function
コンストラクタはできる限り避けるべきでしょう。
ただし、Function
コンストラクタの文字列を解析することで生成された関数内で入れ子にされている関数式や関数宣言は、繰り返し解析されないことに注意してください。例えば:
var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))(); foo(); // 関数本体の文字列で "function() {\n\talert(bar);\n}" の部分は再解析されません
関数宣言はとても簡単に (しばしば意図せずに) 関数式に変化します。関数宣言は以下のような時には関数宣言ではなくなります。
var x = 0; // ソース要素 if (x === 0) { // ソース要素 x = 10; // ソース要素ではない function boo() {} // ソース要素ではない } function foo() { // ソース要素 var y = 20; // ソース要素 function bar() {} // ソース要素 while (y === 10) { // ソース要素 function blah() {} // ソース要素ではない y++; // ソース要素ではない } }
// 関数宣言 function foo() {} // 関数式 (function bar() {}) // 関数式 x = function hello() {} if (x) { // 関数式 function world() {} } // 関数宣言 function a() { // 関数宣言 function b() {} if (0) { // 関数式 function c() {} } }
ES2015 で始まった strict モードでは、ブロック内の関数はそのブロックに新しいスコープを形成します。ES2015 より前では、ブロックレベル関数は strict モードでは禁止されています。
'use strict'; function f() { return 1; } { function f() { return 2; } } f() === 1; // true // 非 strict モードでは f() === 2
一言、使わないでください。
非 strict コードでは、ブロック内の関数宣言は奇妙な動作をします。次の例を見てください。
if (shouldDefineZero) { function zero() { // 危険: 互換性リスク console.log("This is zero."); } }
ES2015 では shouldDefineZero
が false の場合、このブロックが実行されることはないので、zero
は決して定義されないとされています。しかし、これは標準において新しいパーツです。歴史的には、このことは仕様とならないまま残されていました。いくつかのブラウザーでは、ブロックが実行されてもされなくても、zero
を定義したでしょう。
strict モードでは、ES2015 をサポートする全てのブラウザーは、これを同じように扱います。zero
は shouldDefineZero
が true の場合のみ定義され、かつ if
ブロックのスコープに限られます。
条件付きで関数を定義するより安全な方法は、変数に関数式を代入することです。
var zero; if (shouldDefineZero) { zero = function() { console.log("This is zero."); }; }
次の関数は、数値の先頭にゼロを足して固定長にした形で表される文字列を返します。
// この関数は先頭にゼロを足して固定長にした文字列を返す function padZeros(num, totalLen) { var numStr = num.toString(); // 戻り値を文字列に初期化する var numZeros = totalLen - numStr.length; // ゼロの数を計算する for (var i = 1; i <= numZeros; i++) { numStr = "0" + numStr; } return numStr; }
次の文で padZeros 関数を呼び出します。
var result; result = padZeros(42,4); // "0042" を返す result = padZeros(42,2); // "42" を返す result = padZeros(5,4); // "0005" を返す
typeof
演算子を使うと関数が存在するかどうかを確かめる事ができます。次の例では、window
オブジェクトが noFunc
という関数のプロパティを持つかどうかを確かめるためのテストが行われます。もし持っていたら、それが使われます。そうでなければ、他の行動が取られます。
if ('function' == typeof window.noFunc) { // noFunc() を使う } else { // 何か他のことをする }
if
のテストの中で、noFunc
への参照が使われているのに注目してください。関数名の後に括弧 "()" が無いので、実際の関数は呼ばれません。
仕様書 |
---|
{{SpecName('ESDraft', '#sec-function-definitions', 'Function definitions')}} |
{{Compat("javascript.functions")}}