コーディングにおいて、不可欠なコンセプトが関数です。関数を使用することで、特定のタスクをこなすコードを定義し、保持しておいて、いつでも簡単なコマンドで呼び出すことを可能にしてくれます。同じコードを何度も打たなければならないよりとっても簡単です。この記事では関数の書き方や、関数を実行する方法、定義の仕方、スコープ、引数といった関数に関する基礎を学びます。

前提知識: 基本的なコンピュータの知識、 HTML と CSS への理解、JavaScript の第一歩
目的: JavaScript の関数についての基礎を理解する。

関数はどこにありますか?

JavaScript の中で、関数はあらゆるところに見つかるでしょう。実際、これまでのところすべての場面で関数を使用してきました。これについてはあまり触れてきませんでした。しかし、今こそ明確に関数について話し始め、本当に構文を探索する時期です。

ほとんどの場合、一対のかっこを使用するJavaScript構造体を使用します。またあなたが関数を利用しているのであれば、forループwhile と do ... while ループ、またはif..elseステートメントのような一般的な組み込みの言語構造を使用していません

ブラウザ組み込み関数

このコースではブラウザーに組込まれた関数をたくさん使ってきました。毎回テキスト文字列を操作したときには、こんな風に:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');
console.log(newString);
// the replace() string function takes a string,
// replaces one substring with another, and returns
// a new string with the replacement made

あるいは毎回配列を操作したときには:

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
console.log(madeAString);
// the join() function takes an array, joins
// all the array items together into a single
// string, and returns this new string

また毎回乱数を作成したときには:

var myNumber = Math.random();
// the random() function generates a random
// number between 0 and 1, and returns that
// number

...関数を使っていたのです!

メモ: これらの機能に慣れるために、必要なときにはこういった行をあなたのブラウザのJavaScriptコンソールにいつでも入力してみて下さい。

JavaScript言語にはたくさんの組込み関数があるので、いろいろなあなたのやりたい事を、全部をあなた自身で全部書かなくてもすみます。実は、あなたが呼び出して起動(走らせたり実行する事の別の言い方)するコードのいくつかは、JavaScriptでは書けない、ブラウザー組込み関数です — こういった関数の多くは背後のブラウザのコードを呼び出していて、これらはJavaScriptのようなWeb言語ではなく、大半がC++のような低レベルのシステム言語で書かれています。

ブラウザ関数のいくつかはJavaScript言語の核に含まれない事を心に留めておいて下さい — いくつかはブラウザAPIの一部として定義されていて、もっと多くの機能を提供すべくデフォルトの言語の上で構築されています(詳しくは私たちのコースのこの以前のセクションを見て下さい)。ブラウザAPIのもっと詳しい使い方については、後の方のモジュールで見ていく事になるでしょう。

関数とメソッド

この先に進む前に片付けておくべき事柄が一つあります — 技術的に言えば、ブラウザ組込み関数は関数ではありません — それらはメソッドです。ちょっと怖ろしげで意味不明な言葉かもしれませんが、心配しなくて大丈夫 — 関数とメソッドという言葉はおおよそ同じような意味で使えます。少くとも我々の目的、今の学習段階では。

区別としては、メソッドはオブジェクトの中で定義された関数になります。ブラウザ組込み関数(メソッド)と変数(プロパティと呼ばれます)は構成されたオブジェクトの中にあって、コードをより効率的で扱いやすいようにしています。

構成されたJavaScriptオブジェクト内部の働きについては、まだ知る必要はありません — この後のモジュールで、オブジェクト内部の働きや自分でオブジェクトを作る方法について教える段階になってから覚えれば大丈夫です。今のところは、Webのあちこちにある関連したリソースを見ていると、メソッドと関数が混在している事があるとわかってもらいたいだけです。

カスタム関数

このコースのここまででもたくさんのカスタム関数を見てきました — ブラウザの内部でではなくあなたのコードの中で定義された関数です。独自の名前の直後にカッコがついてるものを見かけたら、それはカスタム関数を使っているという事です。 繰返しの記事で出てきたrandom-canvas-circles.html の例(ソースコードはこちら)では、 独自に作った draw() 関数が含まれていました。こんなやつです:

function draw() {
  ctx.clearRect(0,0,WIDTH,HEIGHT);
  for (var i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = 'rgba(255,0,0,0.5)';
    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
    ctx.fill();
  }
}

この関数は、<canvas> エレメントの内にランダムな円を100描きます。同じ事をやりたい時には、いつでもこんな具合に関数を起動するだけです

draw();

繰り返しをする毎に何度も同じコードを書き上げるのではなく。関数にはあなたが書きたいどんなコードでも含められます — 関数の中から他の関数を呼ぶことだってできます。例に挙げた上の関数では、random()関数を3回呼んでいて、random関数は以下のコードで定義されています:

function random(number) {
  return Math.floor(Math.random()*number);
}

ブラウザ組込みの Math.random() は0から1までの間の10進数の乱数を作成するだけなので、私たちにはこの関数が必要でした。私たちは0から指定した数にわたる乱数が欲しかったのです。

関数の呼び出し

もうよくご存知でしょう、でも念のため … 定義した後で実際に関数を使うには、関数を走らせ — あるいは起動し — なければなりません。これはコードのどこかに関数の名前、直後にカッコの組を書けばできます。

function myFunction() {
  alert('hello');
}

myFunction()
// calls the function once

匿名関数

ちょっと違う形式で定義されて起動される関数を見かける事があるでしょう。ここまでは関数をこのように作ってきました:

function myFunction() {
  alert('hello');
}

でも名前のない関数を作る事もできます:

function() {
  alert('hello');
}

これは匿名関数と呼ばれます — 名前がないんです! それだけでは何もしません。無名関数はよくイベントハンドラで使われていて、例えば以下では関連づけられたボタンがクリックされるたび、関数の中のコードが走ります:

var myButton = document.querySelector('button');

myButton.onclick = function() {
  alert('hello');
}

上の例では、ページの中に選択してクリックするための<button>エレメントが存在しなければならないでしょう。あなたはこのような例をここまでのコースで見てきましたし、ここから先の記事でもっと学習し、使い方を見ていく事になります。

匿名関数を変数の値として代入する事もできます:

var myGreeting = function() {
  alert('hello');
}

この関数は次のように起動できます:

myGreeting();

関数に名前をつけたような効果があります。また関数を複数の変数の値として代入する事もできます:

var anotherGreeting = function() {
  alert('hello');
}

結果、この関数はどちらの方法でも起動できます

myGreeting();
anotherGreeting();

ですがこれは混乱するだけなので、やらないように! 関数を作成するときはこの形式でやった方が良いです:

function myGreeting() {
  alert('hello');
}

匿名関数は主にイベント発火 — ボタンがクリックされたとか — のレスポンスとして、一連のコードを走らせるだけのような場合に、イベントハンドラとして使われます。くりかえしですが、こんな具合です:

myButton.onclick = function() {
  alert('hello');
  // I can put as much code
  // inside here as I want
}

関数の引数

関数には実行する時に引数が必要なものがあります — 関数のカッコとカッコの間に書かなければならない値で、関数が正しい仕事をするのに必要とされます。

メモ: 引数は、パラメータ、プロパティ、アトリビュート(属性)などと呼ばれる場合もあります。

例えばブラウザ組込み関数 Math.random() は引数を必要としません。 呼ばれるといつも0から1までの乱数を返します:

var myNumber = Math.random();

ですがブラウザ組込みの文字列関数 replace() は二つの引数が必要です — 主文字列から探すべき部分文字列と、部分文字列を置換する文字列です:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');

メモ: 複数の引数を指定するときは、カンマで区切って書きます

引数には省略可能 — 書かなくても良い — なものもある事に触れておくべきでしょう。省略された場合、関数はだいたいデフォルトに規定された動作を行ないます。例えば、配列の join() 関数のパラメータは省略可能です:

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
// returns 'I love chocolate frogs'
var madeAString = myArray.join();
// returns 'I,love,chocolate,frogs'

もし結合/区切り文字を指定する引数が省略された場合、デフォルトとしてカンマが使われます。

関数のスコープと競合

スコープという言葉について説明しておきましょう — 関数を扱う際にはとても大切な概念です。関数を作成するとき、関数の中で定義されている変数や関数は、内部でそれぞれ独自のスコープというものを持ちます。これはそれぞれが独自の小部屋に閉じ込められていて、別の関数の内部から、あるいはこの関数の外部のコードから触れられくなる事を意味しています。

あなたの全ての関数の外側、一番の外側を、グローバルスコープと呼びます。グローバルスコープで定義された値は全て、コードのどこからでもアクセスできます。

JavaScriptがこう作られているのにはいくつも理由があります — が、主な理由はセキュリティと組織化のためです。時には変数にコードのどこからでもアクセスされないようにしたい場合もあるでしょう — どこかから呼び込んだ外部スクリプトが、あなたのコードをおかしくして問題を起す場合があるかもしれません。別の場所でたまたま同じ名前の変数を使っていて、衝突していたために。これは悪意をもってわざとやっている場合や、単なる偶然の場合もあります。

そうですね、例えばあるHTMLファイルが二つの外部 JavaScript ファイルを呼び出しているとして、そのどちらも同じ名前の変数と関数を定義しているとします:

<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
  greeting();
</script>
// first.js
var name = 'Chris';
function greeting() {
  alert('Hello ' + name + ': welcome to our company.');
}
// second.js
var name = 'Zaptec';
function greeting() {
  alert('Our company is called ' + name + '.');
}

あなたが呼び出したいのはどっちも greeting()関数ですが、あなたには second.js ファイルの greeting() 関数しかアクセスできません — HTMLソースの後の方で呼ばれているので、ここで定義した変数や関数が first.js のものを上書きしてしまいます。

: この例を GitHubでライブ実行 できます(ソースコードはこちら).

あなたのコードの部品を関数の中に隔離するとこのような問題を避けられるので、これが一番良いやりかたと考えられています。

これは動物園みたいなものです。ライオン、シマウマ、トラ、ペンギンはそれぞれの檻の中にいて、それぞれの檻の中のものにしか触れられません — 関数のスコープと同じ事です。もし彼等が他の檻の中に侵入できたら問題が起きることでしょう。良くて、知らない住人に囲まれて気まずい思いをする — 寒くて水だらけのペンギンの檻に入ったライオンやトラは酷い気分になるでしょう。最悪の場合、ライオンやトラはペンギンを食べてみようとするかも!

動物園の管理人はグローバルスコープみたいなものです — 管理人は全ての檻の鍵を持っていて、エサを補充し、動物にうんざりし、などなど。

アクティブラーニング:  scopeで遊んでみよう

スコープを示すための実際の例を見てみましょう。

  1. まず function-scope.html の例のローカルコピーを作成します。これには a()b() という2つの関数と、xyzの3つの変数が含まれます。これらの変数のうち2つは関数内で定義され、もう1つはグローバルスコープ内で定義されます。また output() という3番目の関数も含まれています。この関数は単一のパラメータを取り、ページの段落に出力します
  2. ブラウザとテキストエディタでサンプルを開きます
  3. ブラウザの開発者ツールでJavaScriptコンソールを開きます。 JavaScriptコンソールで、次のコマンドを入力します。
    output(x);
    変数xの出力値が画面に表示されるはずです。
  4. コンソールに次のように入力してみてください
    output(y);
    output(z);
    どちらも、 "ReferenceError: y is not defined"の一行のエラーが返されるはずです。なぜでしょうか? 関数スコープのため、つまり yza()b() 関数の中でロックされているので、globalスコープから呼び出されたときにはoutput()はそれらにアクセスできません。
  5. しかし、別の関数の中から呼び出されたときはどうでしょうか? a()b() を次のように編集してみてください:
    function a() {
      var y = 2;
      output(y);
    }
    
    function b() {
      var z = 3;
      output(z);
    }
    コードを保存してブラウザに再ロードしてから、JavaScriptコンソールから a()b() 関数を呼び出してみてください。
    a();
    b();
    ページに yz の値の出力が表示されます。output() 関数が他の関数の中、つまり表示される変数が定義されているのと同じスコープでそれぞれ呼び出されているので、これはうまくいきます。output() 自体はグローバルスコープで定義されているので、どこからでも利用できます。
  6. 今度は次のようにコードを更新してみてください:
    function a() {
      var y = 2;
      output(x);
    }
    
    function b() {
      var z = 3;
      output(x);
    }
    保存してもう一度読み込み、JavaScriptコンソールでもう一度試してみてください:
    a();
    b();
    a()b() の両方の呼び出しは x の値、つまり1が出力されます。これは x がグローバル変数であり、すべてのコード内どこでも利用可能であるため、 output() の呼び出しが x と同じスコープではなくてもうまく動きます。
  7. 最後に、次のようにコードを更新してみてください:
    function a() {
      var y = 2;
      output(z);
    }
    
    function b() {
      var z = 3;
      output(y);
    }
    保存してもう一度読み込み、JavaScriptコンソールでもう一度試してみてください:
    a();
    b();
    今度は a()b() の両方の呼び出しで、迷惑な "ReferenceError: z is not defined" エラーが返されます - これは output() 呼び出しと、出力しようとしている変数が同じ関数のスコープにない、つまりこれらの関数呼び出しからは変数が参照できない状態だからです。

: 同じスコープルールはループ (for() { ... } など) と条件ブロック (if() { ... }など) には適用されません。それらは非常によく似ていますが、同じものではありません。混乱しないように注意してください。

: ReferenceError: "x" is not defined というエラーは、あなたが遭遇する最も一般的なエラーの1つです。このエラーが発生し、問題の変数が定義されていると確信できる場合は、変数の範囲を確認してください。

関数の中の関数

別の関数内であっても、どこからでも関数を呼び出すことができます。 これは、コードをきれいにする方法としてよく使われます。大きな複雑な関数がある場合は、いくつかのサブ関数に分解すれば分かります。

function myBigFunction() {
  var myValue;

  subFunction1();
  subFunction2();
  subFunction3();
}

function subFunction1() {
  console.log(myValue);
}

function subFunction2() {
  console.log(myValue);
}

function subFunction3() {
  console.log(myValue);
}

関数内で使用されている値が適切に範囲内にあることを確認してください。上記の例では ReferenceError: myValue is not definedというエラーが発生します。myValue変数は関数呼び出しと同じスコープで定義されていますが、関数定義内では定義されていないためです。従って実際のコードは関数が呼び出されたときに実行されます。これを動くようにするには、次のように関数に値を渡す必要があります。

function myBigFunction() {
  var myValue = 1;
      
  subFunction1(myValue);
  subFunction2(myValue);
  subFunction3(myValue);
}

function subFunction1(value) {
  console.log(value);
}

function subFunction2(value) {
  console.log(value);
}

function subFunction3(value) {
  console.log(value);
}

まとめ

この記事では関数の背後にある基本的な概念を探り、次に実用的な方法を習得し、独自のカスタム関数を構築する手順を紹介しました。

関連情報

このモジュール内の文書

 

ドキュメントのタグと貢献者

このページの貢献者: silverskyvicto, i12o, mfuji09, chameleonhead
最終更新者: silverskyvicto,