HTML Living Standard  第3部 Javascript 6 関数


 

6 関数

関数とはよく使う処理をユーザー定義関数としてにまとめて使用することができます。

関数定義はその関数を呼び出す前であればどこにでも記述できますが、一般的には head 要素内もしくは外部ファイル(.js ファイル)で定義します。

6.1  関数

よく使う処理をまとめて使用するために宣言します。

関数 の構文

よく使う処理をまとめて使用するために宣言する。

function 関数名([引数, [引数, [..., [引数]]]) {
  文;
  return [値];
}

function 文で作成された関数は Function オブジェクトであり、Function オブジェクトの全てのプロパティ、メソッド、振る舞いを持ちます。

関数は最大 255 の引数を持てます。

また、return 文で値を返すことができます。指定しない場合は、undefined を返します。

記述例
var x = square(3);  // 前方参照も可

function square(n) {
  return n * n;
}
なお、変数を定義するとき var を使用すると、通常はグローバル変数となりますが、関数内で宣言した場合はローカル変数となります。
記述例
var x = 10;  // グローバル変数 x に 10 を代入
var y = 10;  // グローバル変数 y に 10 を代入

func();

// x は 10
// y は 5

function func() {
  var x = 5;  // ローカル変数 x に 5 を代入
  y = 5;      // グローバル変数 y に 5 を代入
}

6.2  関数式

6.2.1  関数式

関数は関数式によって作成することもできます。関数式は、関数宣言と似ており、同じ構文を持っています。また、関数式は関数ローカル名を省略し無名にできます。

なお、関数宣言のときと違い前方参照は動作しませんので、使用する前に定義する必要があります。

関数式 の構文

よく使う処理をまとめて使用するために宣言する。

var 関数名 = function [関数ローカル名]([引数, [引数, [..., [引数]]]) {
  文;
  return 戻り値;
}
記述例
var square = function(n) { return n * n };

var x = square(3);  // x は 9 になる

関数式は関数ローカル名をつけることもでき、再起呼び出し(関数内で自身を参照すること)などで使用することができます。

記述例
var factorial = function fac(n) { return n < 2 ? 1 : n*fac(n-1) };

var x = factorial(3);  // x は 6 になる
実行例

6.2.2  アロー関数式

アロー関数は関数式よりもさらに短い構文を持ちます。また、アロー関数は、常に 匿名関数 です。

アロー関数式 の構文

よく使う処理をまとめて使用するために宣言する。

return 文に記述した値が関数値となります。また、式の場合は式の値自体が関数値となります。

([引数, [引数, [..., [引数]]]) => { 文; return 戻り値; }
([引数, [引数, [..., [引数]]]) => 式;
記述例
var square = (n) => n * n;
var sum = (a, b, c) => {
            return a + b + c;
          }

var x = square(3);   // x は 9 になる
var y = sum(1,2,3);  // y は 6 になる
実行例

6.3  引数

(1) 仮引数

関数を呼ぶ側の引数(実引数)は、実際の関数の引数(仮引数)に順に設定されます。

記述例
function sum(x, y) {  // x は 5、y は 6 になる
  return x + y;
}
var t = sum(5, 6);

x に 5、y に 6 が設定され 5 + 6 となり結果は 11 になります。

実行例

ただし、実引数の数が、仮引数の数よりも少ない場合は、仮引数に undefined が設定されます。

記述例
function sum(x, y) {  // y は undefined になる
  return x + y;
}
var t = sum(5);

x には 5 が設定されますが、y には何も設定されないので undefined となり加算ができなくて、NaN になります。

実行例

このような関数に値が渡されない場合や undefined が渡されるような場合には、デフォルト値で初期化される仮引数を指定できます。

もちろん、デフォルト値が指定されている仮引数でも、実引数として値を指定すれば指定した値が仮引数に設定されます。

記述例
function sum(x, y = 1) {  // 実引数が指定されていなければ y は 1 になる
  return x + y;
}
var t = sum(5);      // 6
var t = sum(5, 6);   // 11

一つ目の例は x には 5 が設定されますが、y には何も設定されないのでデフォルトの 1 が設定され、5 + 1 となり結果は 6 になります。

二つ目の例は y も指定されているので、 5 + 6 となり結果は 11 になります。

実行例

(2) arguments

また、関数の引数は、arguments という配列のようなオブジェクトでも管理されます。ただし、arguments は、数値のインデックスと length プロパティがあることで、配列のようなものとなってはいますが、配列操作のメソッドはまったく持っていません。


プロパティ
calleeR/O現在実行している関数
lengthR/O関数に渡された引数の数
arguments[]R/O関数に渡された引数

ただし、アロー関数では arguments は作られません。

記述例
function sumA() {
  let s = 0;
  for (let i = 0 ; i < arguments.length ; i++)
    s += arguments[i];
  return s;
}
function sumB(a, b) {
  let s = 0;
  for (let i = 0 ; i < arguments.length ; i++)
    s += arguments[i];
  return s;
}
var tA = sumA(1, 2, 3, 4);  // tA は 10 になる
var tB = sumB(1, 2, 3, 4);  // tB は 10 になる

sumB は、引数 a に 1、b に 2 が代入されますが、さらに すべての引数が arguments に配列のように代入されます。

実行例

arguments を他の関数に引数として渡すときには注意が必要です。

arguments を引数として受け取った関数(sub11 や sub21)には、arguments を1つ目の要素とした arguments が渡ります。

記述例
function sub1() {
  return sub11(arguments);    // arguments = [1,2,3,4]
}
function sub11() {    // arguments = [[1,2,3,4]]
  let s = 0;
  for (let i = 0 ; i < arguments.length ; i++)
    s += arguments[i];
  return s;
}
function sub2() {
  return sub21(arguments);    // arguments = [1,2,3,4]
}
function sub21() {    // arguments = [[1,2,3,4]]
  let s = 0;
  let args = arguments[0];    // args = [1,2,3,4]
  for (let i = 0 ; i < args.length ; i++)
    s += args[i];
  return s;
}

var t1 = sub1(1,2,3,4);    // 0[object Arguments]
var t2 = sub2(1,2,3,4);    // 10
実行例

(3) 残余引数

arguments はすべての引数を持ちますが、指定した引数以外の残りの引数全てを持つ変数(残余引数)を定義することもできます。... の後に記述された変数は Array インスタンスとして生成され、残りの引数がすべて代入されます。

記述例
function sumA(...args) {
  var s = 0;
  for (var i = 0 ; i < args.length ; i++)
    s += args[i];
  return s;
}
function sumB(a, b, ...args) {
  var s = 0;
  for (var i = 0 ; i < args.length ; i++)
    s += args[i];
  return s;
}
var tA = sumA(1, 2, 3, 4);  // tA は 10 になる
var tB = sumB(1, 2, 3, 4);  // tB は 7 になる

sumB は、引数 a に 1、b に 2 が代入されます。そして、残りの引数が ... の後に記述された変数に配列として代入されます。

実行例

6.4  関数のネスト

関数の内部に関数をネストにすることができます。ネストされた内側の関数は、外側の関数内の文からのみアクセスでき、外部から呼び出すことはできません。(ただし、内部の関数を戻り値とすると外部からも呼び出すことができます(6.5 クロージャ 参照)。)

Javascript では、関数内で定義された変数はローカル変数となり関数の外側からは参照できません。よって、外側の関数は内側の関数の引数と変数を使うことはできません。しかし、内側の関数は外側の関数の引数と変数を使うことができます。

記述例
function addSquares(a, b) {
  function square(x) {
    return x * x;
  }
  return square(a) + square(b);  // 2 * 2 + 3 * 3
}
var x = addSquares(2, 3);  // x は 13 になる
実行例

外部と内部両方の関数に対し引数を指定することもできます。

記述例
function multiply(x) {
  function by(y) {
    return x * y;
  }
  return by;
}
var multiplyBy = multiply(5);  // multiplyBy は 5 * ? の意味になる
var a = multiplyBy(3);  // a は 15 (5*3) になる
var b = multiplyBy(4);  // b は 20 (5*4) になる
var c = multiply(7)(3); // c は 21 (7*3) になる
実行例

6.5  クロージャ

クロージャとは、定義時のコンテキストとは異なるコンテキスト上に持ち出された関数です。つまり、ネストされた外側の関数から return 文によって返され、グローバル変数に代入された、内側の関数のことです。

なお、ここでいう「コンテキスト」とは、その関数がおかれた環境のことです。

記述例
var createCounter = function() {
  var cnt = 0;               // ローカル変数
  return function() {
    return ++cnt;
  };
};

var counter = createCounter();  // counter = function() {return ++cnt;};
var x = counter();  // x は 1 になる
var y = counter();  // y は 2 になる

var z = cnt;        // z は undefined になる(cnt は createCounter 外からは参照できない)

上の例は、普通に考えたら、createCounter 関数の実行が完了した時点で、その内部変数である cnt は破棄されてしまいそうですが、counter() で、加算された cnt が返ってくることで分かるように、明らかに cnt は保持され続けています。

これは、counter に代入された関数がクロージャになっているからです。counter に代入された関数は、定義時のコンテキストは createCounter 関数のローカルスコープですが、変数 counter のコンテキストはグローバルスコープです。つまり、定義時のコンテキストとは異なるコンテキスト上に持ち出された関数になっています。

クロージャは、関数とその関数の定義時のコンテキストをセットにした特殊なオブジェクトです。そのため、createCounter 関数の実行が完了しても、その内部変数 cnt はクロージャ内に保持されているため参照し続けることが可能なのです。

実行例

なお、先の例の multiply 関数の返す by 関数もクロージャです。

6.6  定義済み関数

オブジェクトに関連付けられていない、定義済みのトップレベル関数が数種類あります。

6.6.1 数値変換

(1) Number()

引数で渡された文字列などを数値に変換します。

正負記号(+ か -)、数 (0-9) 、小数点、指数文字(E や e)を指定できます。また、「0x」または「0X」で始まる場合は16進数として解釈されますが、この場合は符号は付けられません。

文字列の前後には、空白があっても問題ありません。

Number(exp)

文字列などを数値に変換し、その値(数値に変換できない場合は NaN や Infinity)を返す。

引数 exp:文字列や日付オブジェクトなど

戻り値:変換された数値、NaN(数値に変換できない)、Infinity(無限)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=Number(" +123  ");
    document.getElementById("d2").textContent=Number("-12.5");
    document.getElementById("d3").textContent=Number("");
    document.getElementById("d4").textContent=Number("0x1F");
    document.getElementById("d5").textContent=Number("1e10");
    document.getElementById("d6").textContent=Number("XXX");
    document.getElementById("d7").textContent=Number("1e1000");
  </script>
実行例

(2) parseFloat()

文字列を解析し浮動小数点数を返します。正負記号(+ か -)、数 (0-9) 、小数点、指数以外の文字があった場合は、その箇所より前の値を返してそれ以降の文字すべてを無視します。文字列の前後に空白があっても問題ありません。

parseFloat(string)

string を解析し、数値ならば浮動小数点数を返す。

引数 string:解析する文字列

戻り値:浮動小数点数、NaN(数値に変換できない)、Infinity(無限)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=parseFloat("  3.14  ");
    document.getElementById("d2").textContent=parseFloat("0.314E+1");
    document.getElementById("d3").textContent=parseFloat("3.14X");
    document.getElementById("d4").textContent=parseFloat(3.14);
    document.getElementById("d5").textContent=parseFloat("X");
    document.getElementById("d6").textContent=parseFloat(NaN);
    document.getElementById("d7").textContent=parseFloat(undefined);
    document.getElementById("d8").textContent=parseFloat(Infinity);
  </script>
実行例

(3) parseInt()

解析値を解析し整数を返します。2つ目の引数には何進数として解析するかを指定します。省略すると10進数と見なされます。

解析値に基数で使用しない文字が現れると解析を終了しそれまでの値を返します。また、「0x」または「0X」で始まるときは、16進数として見なします。

parseInt(string[, radix])

string を解析し整数を返す。radix には何進数かを指定する。

引数 string:判定する値(「0x」または「0X」で始まるときは、16進数として見なされる)

引数 radix:何進数かの値(規定値:10)

戻り値:浮動小数点数、NaN(数値に変換できない)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=parseInt("  10A  ");
    document.getElementById("d2").textContent=parseInt("  10A", 16);
    document.getElementById("d3").textContent=parseInt(" 0x10A");
    document.getElementById("d4").textContent=parseInt(10.5);
    document.getElementById("d5").textContent=parseInt("X");
    document.getElementById("d6").textContent=parseInt(NaN);
    document.getElementById("d7").textContent=parseInt(undefined);
    document.getElementById("d8").textContent=parseInt(Infinity);
  </script>
実行例

6.6.2 文字列変換

引数で渡された数値などを文字列に変換ししたり、エスケープ文字を置換します。

(1) String()

数値を文字列に変換します。

String(number)

数値を文字列に変換する。

引数 number:数値

戻り値:文字列

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=String(+123);
    document.getElementById("d2").textContent=String(-12.5);
    document.getElementById("d3").textContent=String(0x1F);
    document.getElementById("d4").textContent=String(12500000000000000000000);
    document.getElementById("d5").textContent=String(Number.NaN);
    document.getElementById("d6").textContent=String(Infinity);
  </script>
実行例

(2) encodeURI()

特定の文字をそれぞれ UTF-8 エンコーディングで表現した 1 個から 4 個のエスケープシーケンスに置き換えます。

encodeURI(URI)

特定の文字をそれぞれ UTF-8 符号化で表現した 1 個から 4 個のエスケープシーケンスに置き換える。

引数 URI:エンコード前の URI

戻り値:特定の文字をエスケープシーケンスに置き換えた文字列

次の文字を除くすべての文字をエスケープシーケンスに置き換える。

アルファベット 数字 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=encodeURI("<tag>");
    document.getElementById("d2").textContent=encodeURI("漢字");
    document.getElementById("d3").textContent=encodeURI("\" +10%#\"");
  </script>
実行例

適当な文字を入力して Enter キーを押してください。エスケープシーケンスに置き換えた文字列が表示されます。



エスケープシーケンス
  


(3) encodeURIComponent()

特定の文字をそれぞれ UTF-8 エンコーディングで表現した 1 個から 4 個のエスケープシーケンスに置き換えます。

ただし、「/」「:」「&」「+」「=」などもエンコードする為、URI 全体に適用すると URI として機能しなくなります。

encodeURIComponent(URI)

特定の文字をそれぞれ UTF-8 符号化で表現した 1 個から 4 個のエスケープシーケンスに置き換える。

引数 URI:エンコード前の URI

戻り値:特定の文字をエスケープシーケンスに置き換えた文字列

次の文字を除くすべての文字をエスケープシーケンスに置き換える。

アルファベット 数字 - _ . ! ~ * ' ( )

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=encodeURI("<tag>");
    document.getElementById("d2").textContent=encodeURI("漢字");
    document.getElementById("d3").textContent=encodeURI("\" +10%#\"");
  </script>
実行例

適当な文字を入力して Enter キーを押してください。エスケープシーケンスに置き換えた文字列が表示されます。



エスケープシーケンス
  


(4) decodeURI()

encodeURI 関数などによって事前に作成された URI のエスケープシーケンスをデコードします。

decodeURI(URI)

URI のエスケープシーケンスをデコードする。

引数 URI:エスケープシーケンス

戻り値:デコードされた文字列

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=decodeURI("%3Ctag%3E");
    document.getElementById("d2").textContent=decodeURI("%E6%BC%A2%E5%AD%97");
    document.getElementById("d3").textContent=decodeURI("%22%20%2B10%25%23%22");
  </script>
実行例

エスケープシーケンスを入力して Enter キーを押してください。デコードされた文字列が表示されます。



デコードされた文字列
  


(5) decodeURIComponent()

encodeURIComponent 関数などによって事前に作成された URI のエスケープシーケンスをデコードします。

decodeURIComponent(URI)

URI のエスケープシーケンスをデコードする。

引数 URI:エスケープシーケンス

戻り値:デコードされた文字列

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=decodeURIComponent("%3Ctag%3E");
    document.getElementById("d2").textContent=decodeURIComponent("%E6%BC%A2%E5%AD%97");
    document.getElementById("d3").textContent=decodeURIComponent("%22%20%2B10%25%23%22");
実行例

エスケープシーケンスを入力して Enter キーを押してください。デコードされた文字列が表示されます。



デコードされた文字列
  


6.6.3 日時

現在日時を文字列として取得します。

(1) Date()

現在日時を文字列として取得します。

Date()

現在日時を文字列として取得する。

戻り値:現在日時の文字列

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=Date();
  </script>
実行例

6.6.4 スクリプト実行

文字列の評価をします。

(1) eval()

文字列として書き表された JavaScript のコードを実行します。

eval(statements)

文字列として書き表された JavaScript の命令を評価し、その値を返す。

引数 statements:JavaScript の命令を記述した文字列

戻り値:評価された結果(式などの値)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    var a = eval("2+3");  // a は 5 になる
    var x = 2;
    var y = 3;
    var b = eval("x+y");  // b は 5 になる
    var s = "if(z){x+y;}else{x-y;}";
    var z = true;
    var c = eval(s);     // c は 5 になる(z が true で、s の評価をする)
    z = false;
    var d = eval(s);     // d は -1 になる(z が false で、s の評価をする)

    document.getElementById("d1").textContent=a;
    document.getElementById("d2").textContent=b;
    document.getElementById("d3").textContent=c;
    document.getElementById("d4").textContent=d;
  </script>
実行例

6.6.5 値評価

値の評価をします。

(1) isFinite()

有限の数値に変換できるかどうかを判定します。変換できるとき true になります。

isFinite(value)

有限の数値に変換できるかどうかを判定し、その結果を返す。

引数 value:判定する値

戻り値:true(数値に変換できる)、false(数値に変換できない)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=isFinite(Infinity);
    document.getElementById("d2").textContent=isFinite(NaN);
    document.getElementById("d3").textContent=isFinite(undefined);
    document.getElementById("d4").textContent=isFinite(0);
    document.getElementById("d5").textContent=isFinite(Number.MAX_VALUE);
    document.getElementById("d6").textContent=isFinite(null);
    document.getElementById("d7").textContent=isFinite("0");
  </script>
実行例

(2) isNaN()

数値に変換できないかどうかを判定します。変換できないとき true になります。

isNaN(value)

数値に変換できないかどうかを判定し、その結果を返す。

引数 value:判定する値

戻り値:true(数値に変換できない)、false(数値に変換できる)

記述例
  <div id="d1"></div>
  <!-- 以下略 -->

  <script>
    document.getElementById("d1").textContent=isNaN(NaN);
    document.getElementById("d2").textContent=isNaN(undefined);
    document.getElementById("d3").textContent=isNaN("aaa");
    document.getElementById("d4").textContent=isNaN(Infinity);
    document.getElementById("d5").textContent=isNaN(Number.MAX_VALUE);
    document.getElementById("d6").textContent=isNaN(null);
    document.getElementById("d7").textContent=isNaN("0");
  </script>
実行例