Linux Professional Institute Learning Logo.
main contentにスキップ
  • ホーム
    • 全てのリソース
    • LPI学習教材
    • コントリビューターになる
    • Publishing Partners
    • Publishing Partnerになる
    • About
    • FAQ
    • コントリビューター
    • Roadmap
    • 連絡先
  • LPI.org
034.3 レッスン 2
課題 031: ソフトウェア開発とWebテクノロジー
031.1 ソフトウェア開発の基本
  • 031.1 レッスン 1
031.2 Webアプリケーションのアーキテクチャ
  • 031.2 レッスン 1
031.3 HTTP の基礎
  • 031.3 レッスン 1
課題 032: HTMLドキュメントマークアップ
032.1 HTMLドキュメントの構造
  • 032.1 レッスン 1
032.2 HTMLのセマンティックスとドキュメント階層
  • 032.2 レッスン 1
032.3 HTMLにおける参照と埋め込みリソース
  • 032.3 レッスン 1
032.4 HTMLフォーム
  • 032.4 レッスン 1
課題 033: CSSコンテンツ スタイリング
033.1 CSSの基本
  • 033.1 レッスン 1
033.2 CSSセレクターとスタイルアプリケーション
  • 033.2 レッスン 1
033.3 CSSスタイリング
  • 033.3 レッスン 1
033.4 CSSボックスモデルとレイアウト
  • 033.4 レッスン 1
課題 034: JavaScriptプログラミング
034.1 JavaScriptの実行と構文
  • 034.1 レッスン 1
034.2 JavaScriptデータ構造
  • 034.2 レッスン 1
034.3 JavaScriptの制御構造と関数
  • 034.3 レッスン 1
  • 034.3 レッスン 2
034.4 WebサイトのコンテンツとスタイリングのJavaScript操作
  • 034.4 レッスン 1
課題 035: NodeJSサーバープログラミング
035.1 NodeJSの基本
  • 035.1 レッスン 1
035.2 NodeJS Expressの基本
  • 035.2 レッスン 1
  • 035.2 レッスン 2
035.3 SQLの基本
  • 035.3 レッスン 1
How to get certified
  1. 課題 034: JavaScriptプログラミング
  2. 034.3 JavaScriptの制御構造と関数
  3. 034.3 レッスン 2

034.3 レッスン 2

Certificate:

Web開発の要点

Version:

1.0

Topic:

034 JavaScriptプログラミング

Objective:

034.3 JavaScriptの制御構造と関数

Lesson:

2 of 2

はじめに

開発者は、JavaScript言語が提供する標準的な組み込み関数に加えて、独自のカスタム関数を記述することで、アプリケーションのニーズに適した入力と出力を対応付けることができます。カスタム関数は、基本的に、式の一部として他の場所で使用するためにカプセル化されたステートメントのセットです。

関数はプログラム中のさまざまな場所から呼び出すことができるので、重複したコードを書かないためには、関数を使うのがよい方法です。さらに、ステートメントを関数にまとめることで、JavaScriptプログラミングの中心的な要素であるイベントへのカスタムアクションの結合が容易になります。

関数の定義

プログラムが大きくなってくると、関数を使わずに何をしているのかを整理することが難しくなります。関数はそれぞれプライベートな変数スコープを持っているので、関数の中で定義された変数は、その関数の中でしか使えません。そのため、他の関数の変数と混ざってしまうことはありません。グローバル変数は関数内からもアクセスできますが、入力値を関数に送るには 関数パラメータ を使うのが望ましいでしょう。例として、前のレッスンで習った素数検証器を使ってみましょう。

// A naive prime number tester

// The number we want to evaluate
let candidate = 231;

// Auxiliary variable
let is_prime = true;

// Start with the lowest prime number after 1
let factor = 2;

// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{

  if ( candidate % factor == 0 )
  {
    // The remainder is zero, so the candidate is not prime
    is_prime = false;
    break;
  }

  // The next number that will divide the candidate
  factor++;
}

// Display the result in the console window
if ( is_prime )
{
  console.log(candidate, "is prime");
}
else
{
  console.log(candidate, "is not prime");
}

コードの後半で、ある数字が素数であるかどうかを調べる必要がある場合、すでに書かれたコードを繰り返す必要があります。元のコードに修正や改良が加えられた場合、コードがコピーされた先でも手作業で再現する必要があるため、このような方法はお勧めできません。また、コードを繰り返すことで、ブラウザやネットワークに負荷がかかり、Webページの表示速度が低下する可能性があります。このような方法ではなく、適切なステートメントを関数に移動させてください。

// A naive prime number tester function
function test_prime(candidate)
{
  // Auxiliary variable
  let is_prime = true;

  // Start with the lowest prime number after 1
  let factor = 2;

  // Keeps evaluating while factor is less than the candidate
  while ( factor < candidate )
  {

    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime
      is_prime = false;
      break;
    }

    // The next number that will divide the candidate
    factor++;
  }

  // Send the answer back
  return is_prime;
}

関数の宣言は、 function 文で始まり、その後に関数の名前とパラメータが続きます。関数の名前は、変数の名前と同じルールに従わなければなりません。関数のパラメータは、関数の 引数 とも呼ばれ、カンマで区切られ、括弧で囲まれています。

Tip

関数の宣言に引数を記載することは必須ではありません。関数に渡された引数は、その関数内の配列のような arguments オブジェクトから取り出すことができます。引数のインデックスは0から始まるので、最初の引数は arguments[0] 、2番目の引数は arguments[1] 、というようになります。

この例では、 test_prime 関数の引数は1つだけで、それは、テストする素数の候補である candidate 引数です。関数の引数は変数のように機能しますが、その値は関数を呼び出したステートメントによって割り当てられます。例えば、 test_prime(231) という文は、 test_prime 関数を呼び出し、 candidate 引数に231という値を代入します。この値は、通常の変数のように、関数本体内で利用できます。

呼び出し文が関数のパラメータに単純変数を使用している場合、その値は関数の引数にコピーされます。呼び出し文で使われているパラメータの値を、関数内で使われているパラメータにコピーするこの手順を、 引数の値渡し と呼びます。関数が引数を変更しても、呼び出し文で使われていた元の変数には影響しません。しかし、呼び出し文が関数のパラメータに複雑なオブジェクト(つまり、プロパティやメソッドが付加されたオブジェクト)を引数として使用している場合、それらは 参照渡し となり、関数は呼び出し文で使用していた元のオブジェクトを変更することができてしまいます。

値で渡された引数や、関数内で宣言された変数は、関数の外では見ることができません。つまり、それらのスコープは、宣言された関数の本体に限定されます。それにもかかわらず、関数は通常、関数の外に見える何らかの出力を作成するために採用されます。呼び出した関数と値を共有するために、関数は return 文を定義します。

例えば、前の例の test_prime 関数は、 is_prime 変数の値を返します。したがって、この関数は、元の例で使われていた変数をどこでも置き換えることができます。

// The number we want to evaluate
let candidate = 231;

// Display the result in the console window
if ( test_prime(candidate) )
{
  console.log(candidate, "is prime");
}
else
{
  console.log(candidate, "is not prime");
}

return 文は、その名のとおり、呼び出した関数に制御を戻すものです。したがって、関数内のどこに return 文を置いても、それに続くものは何も実行されません。1つの関数に複数の return 文を含めることができます。この方法は、いくつかのステートメントが条件付きのブロックの中にある場合に便利で、関数が実行されるたびに特定の return ステートメントを実行することもあれば、しないこともあります。

関数の中には、値を返さないものもありますので、 return 文は必須ではありません。関数の内部ステートメントは、その有無にかかわらず実行されますので、関数は、例えば、グローバル変数の値や、参照渡しされたオブジェクトの内容を変更するためにも使用できます。それにもかかわらず、関数が return 文を持たない場合、そのデフォルトの戻り値は undefined に設定されます。これは、値を持たず、書き込むこともできない予約済みの変数です。

関数式

JavaScriptでは、関数は オブジェクト の一種です。そのため、関数は変数のようにスクリプト内で使用することができます。この特性は、 関数式 と呼ばれる別の構文を使って関数を宣言したときに明示されます。

let test_prime = function(candidate)
{
  // Auxiliary variable
  let is_prime = true;

  // Start with the lowest prime number after 1
  let factor = 2;

  // Keeps evaluating while factor is less than the candidate
  while ( factor < candidate )
  {

    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime
      is_prime = false;
      break;
    }

    // The next number that will divide the candidate
    factor++;
  }

  // Send the answer back
  return is_prime;
}

この例と前の例の関数宣言との唯一の違いは、最初の行にあります。 function test_prime(candidate) ではなく、let test_prime = function(candidate) となっていることです。関数式の中で、 test_prime という名前は、関数を含むオブジェクトに使われ、関数自体の名前には使われません。関数式で定義された関数は、宣言構文で定義された関数と同じように呼び出されます。ただし、宣言された関数は宣言の前後を問わずに呼び出すことができるのに対し、関数式は初期化後にのみ呼び出すことができます。変数と同様に、式で定義された関数を初期化前に呼び出すと、参照エラーが発生します。

関数の再帰

カスタム関数は、ステートメントの実行や組み込み関数の呼び出しに加えて、自分自身を含む他のカスタム関数を呼び出すことができます。自分自身から関数を呼び出すことを 再帰関数 と呼びます。解決しようとしている問題の種類によっては、再帰関数を使った方が、ネストしたループを使って反復的な作業を行うよりも簡単な場合があります。

ここまでで、与えられた数が素数かどうかを調べるための関数の使い方がわかりました。ここで、与えられた数の次の素数を見つけたいとします。 while ループを使って候補の数字を増やし、その候補の因数を探す入れ子のループを書くことができます。

// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
  // We are only interested in the positive primes,
  // so we will consider the number 2 as the next
  // prime after any number less than two.
  if ( from < 2 )
  {
    return 2;
  }

  // The number 2 is the only even positive prime,
  // so it will be easier to treat it separately.
  if ( from == 2 )
  {
    return 3;
  }

  // Decrement "from" if it is an even number
  if ( from % 2 == 0 )
  {
    from--;
  }

  // Start searching for primes greater then 3.

  // The prime candidate is the next odd number
  let candidate = from + 2;

  // "true" keeps the loop going until a prime is found
  while ( true )
  {
    // Auxiliary control variable
    let is_prime = true;

    // "candidate" is an odd number, so the loop will
    // try only the odd factors, starting with 3
    for ( let factor = 3; factor < candidate; factor = factor + 2 )
    {
      if ( candidate % factor == 0 )
      {
        // The remainder is zero, so the candidate is not prime.
        // Test the next candidate
        is_prime = false;
        break;
      }
    }
    // End loop and return candidate if it is prime
    if ( is_prime )
    {
      return candidate;
    }
    // If prime not found yet, try the next odd number
    candidate = candidate + 2;
  }
}

let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));

注意していただきたいのは、 while ループに一定の条件(括弧内の true 式)と、ループを止めるタイミングを知るための補助変数 is_prime を使う必要があることです。この解決法は正しいのですが、入れ子になったループを使うことは、同じタスクを実行するために再帰を使うことほどエレガントではありません。

// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
  // We are only interested in the positive primes,
  // so we will consider the number 2 as the next
  // prime after any number less than two.
  if ( from < 2 )
  {
    return 2;
  }

  // The number 2 is the only even positive prime,
  // so it will be easier to treat it separately.
  if ( from == 2 )
  {
    return 3;
  }

  // Decrement "from" if it is an even number
  if ( from % 2 == 0 )
  {
    from--;
  }

  // Start searching for primes greater then 3.

  // The prime candidate is the next odd number
  let candidate = from + 2;

  // "candidate" is an odd number, so the loop will
  // try only the odd factors, starting with 3
  for ( let factor = 3; factor < candidate; factor = factor + 2 )
  {
    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime.
      // Call the next_prime function recursively, this time
      // using the failed candidate as the argument.
      return next_prime(candidate);
    }
  }

  // "candidate" is not divisible by any integer factor other
  // than 1 and itself, therefore it is a prime number.
  return candidate;
}

let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));

next_prime のどちらのバージョンも、唯一の引数として与えられた数( from )の次の素数を返します。再帰的なバージョンでは、前のバージョンと同様に、特殊なケース(すなわち、2以下の数)をチェックすることから始まります。その後、候補を増やしていき、 for ループで因数を探し始めます( while ループがなくなっていることに注意してください)。この時点で、唯一の偶数の素数はすでにテストされているので、候補とその可能な因子を2つ増やしていきます。(奇数に2を加えたものが次の奇数になります)。

例題の for ループから抜け出す方法は2つしかありません。可能性のある因数をすべて試して、候補を割ったときの余りが0になるものがなければ、 for ループは終了し、関数は候補を from の次の素数として返します。そうでなければ、 factor が candidate の整数倍である( candidate % factor == 0 )場合には、 next_prime 関数が再帰的に呼び出され、今度は増加した candidate を from パラメータとして用いて返されます。 next_prime の呼び出しは、1つの候補が最終的に因数を見つけられなくなるまで、互いに積み重ねられます。そして、素数を含む最後の next_prime インスタンスは、その素数を前の next_prime インスタンスに返し、その結果、最初の next_prime インスタンスまで連続していきます。関数の各呼び出しでは、変数に同じ名前を使っていますが、呼び出しは互いに分離されているので、それらの変数はメモリ内で分離されています。

演習

  1. 関数を使うことで、開発者はどのようなオーバーヘッドを軽減できるのでしょうか?

  2. 関数の引数が値で渡される場合と、関数の引数が参照で渡される場合の違いは何ですか?

  3. カスタム関数にreturn文がない場合、どの値が出力として使われますか?

発展演習

  1. 関数を呼び出した際に発生した 不適切な参照エラー(Uncaught Reference Error) の原因は何ですか?

  2. factor 、 from 、および to の3つの引数を受け取る multiples_of という関数を書いてください。この関数の中で、 console.log() という命令を使って、 from と to の間にある factor のすべての倍数を表示してください。

まとめ

このレッスンでは、JavaScriptのコードにカスタム関数を記述する方法を説明しました。カスタム関数を使うと、開発者はアプリケーションを再利用可能なコードの “チャンク” に分割することができ、大規模なプログラムの作成やメンテナンスが容易になります。このレッスンでは、次のような概念と手順を説明しました。

  • カスタム関数を定義する方法:関数宣言と関数式。

  • 関数の入力にパラメータを使用する

  • 関数の出力を設定するために return 文を使用する

  • 関数の再帰

演習の回答

  1. 関数を使うことで、開発者はどのようなオーバーヘッドを軽減できるのでしょうか?

    関数を使うとコードを再利用できるので、コードのメンテナンスが容易になります。また、スクリプトファイルを小さくすることで、メモリやダウンロードの時間を節約することができます。

  2. 関数の引数が値で渡される場合と、関数の引数が参照で渡される場合の違いは何ですか?

    値で渡された場合、引数は関数にコピーされ、関数は呼び出し文の元の変数を変更することができません。参照で渡された場合、関数は呼び出し文で使われた元の変数を操作することができます。

  3. カスタム関数にreturn文がない場合、どの値が出力として使われますか?

    返された値は undefined に設定されます。

発展演習の回答

  1. 関数を呼び出した際に発生した 不適切な参照エラー(Uncaught Reference Error) の原因は何ですか?

    スクリプトファイルで宣言される前に関数が呼び出されました。

  2. factor 、 from 、および to の3つの引数を受け取る multiples_of という関数を書いてください。この関数の中で、 console.log() という命令を使って、 from と to の間にある factor のすべての倍数を表示してください。

      function multiples_of(factor, from, to)
      {
        for ( let number = from; number <= to; number++ )
        {
          if ( number % factor == 0 )
          {
            console.log(factor, "*", number / factor, "=", number);
          }
        }
      }

Linux Professional Insitute Inc. All rights reserved. 学習資料をご覧ください: https://learning.lpi.org
ここでの作成物は、Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Licenseの下でライセンスされています。

次のレッスン

034.4 WebサイトのコンテンツとスタイリングのJavaScript操作 (034.4 レッスン 1)

次のレッスンを読む

Linux Professional Insitute Inc. All rights reserved. 学習資料をご覧ください: https://learning.lpi.org
ここでの作成物は、Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Licenseの下でライセンスされています。

LPIは非営利団体です。

© 2023 Linux Professional Institute(LPI)は、オープンソースプロフェッショナル向けのグローバルな認定基準およびキャリアサポート組織です。200,000人以上の認定保持者を擁する、世界初かつ最大のベンダー中立Linuxおよびオープンソース認定機関です。LPIは180か国以上で認定プロフェッショナルを擁し、複数の言語で試験を実施し、何百ものトレーニングパートナーを擁しています。

私たちの目的は、オープンソースの知識とスキルの認定資格を世界中からアクセスできるようにすることで、誰にとっても経済的で創造的な機会を可能にすることです。

  • LinkedIn
  • flogo-RGB-HEX-Blk-58 Facebook
  • Twitter
  • お問い合わせ
  • 個人情報とCookieポリシー

間違いを見つけたり、このページを改善したいですか? 私たちに知らせてください。.

© 1999–2023 The Linux Professional Institute Inc. All rights reserved.