進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

イベントや関数を使用したアクションの実行

学習の目的

この単元を完了すると、次のことができるようになります。

  • 関数を定義する。
  • 関数宣言と関数式を区別する。
  • 関数を呼び出す。
  • 関数を渡して代入する。
  • Lightning Web コンポーネントでの関数やイベントの使用について説明する。

JavaScript で何かを行う場合、それを実行するのはイベントと関数です。

ランタイムのモデルを覚えていますか? もう少し詳しく説明しましょう。 

ブラウザの JavaScript には、あちこちにイベントが存在します。DOM の各部が、DOM オブジェクトが実行する内容に対応するイベントを生成します。ボタンにはクリックイベントがあり、入力コントロールや選択コントロールには変更イベントがあります。また、表示可能な DOM のほぼすべての部分に、マウスのカーソル操作で (カーソルを置いた場合など) 生じるイベントが存在します。window オブジェクトでさえ、デバイスイベント (モバイルデバイスの動きの検出など) を処理するイベントハンドラがあります。 

Web ページで何かが行われるのは、関数がこれらのイベントにイベントハンドラとして代入されるからです。 

繰り返しになりますが、DOM イベントやブラウザ環境に関連するその他のイベントは、実際には JavaScript のコア言語の一部ではなく、JavaScript のためにブラウザに実装された API です。 

イベントが生じると、エンジンでメッセージが作成されます。前述のイベントキューに入れられるのはこうしたメッセージです。 

キューのメッセージとスタックのフレームスタックが空になると、イベントハンドラが呼び出されます。この動作によって、コールスタック上にフレームというものが作成されます。1 つの関数が別の関数を呼び出すたびに、新しいフレームがスタックに追加され、完了するとフレームがスタックから取り出され、最終的には実際のイベントハンドラのフレームが取り出されます。スタックが空になると、また同じことが繰り返されます。 

関数の定義と代入

JavaScript では、関数は基本的に特殊なオブジェクトです。オブジェクトである関数は、JavaScript の特権階級のメンバーです。関数は変数の値として代入されたり、パラメータとして他の関数に渡されたり、関数から返されたりします。 

関数のライフサイクルには、定義と呼び出しという 2 つの重要なフェーズがあります。

関数が宣言されると、その定義がメモリに読み込まれます。次に、ポインタが変数名、パラメータ名、オブジェクトプロパティのいずれかの形式でその関数に割り当てられます。このことを実行するために数種の構文があるのも驚くことではありません。

関数宣言

宣言とは、function キーワードを使用して関数を作成するステートメントです。実際、オブジェクトコンストラクタの説明で、すでに宣言を見てきました。このコンストラクタが関数です。けれども、コンストラクタ関数はやや特殊なため、旧来のシンプルな関数に戻って、そのしくみを見てみましょう。 

// declare function
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// call function
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

このサンプルコードでは、関数の後に関数名が続き、パラメータが括弧で囲まれています。 

これでもきちんと機能しますが、暗黙のうちにいくつかのことが行われます。まず、関数名が変数名になります。また、変数がそれを囲んでいるコンテキストに暗黙的に代入されます。最後に、この関数を、以下のように宣言される前にコールすることができます。ここでは、calculateGearRatio が宣言の前の行で呼び出されています。 

// call function
let gearRatio = calculateGearRatio(42, 30);
// function is declared after the line it is called
// this is allowed in function declaration
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
console.log(gearRatio); // 1.4

関数式

関数式は宣言と同じことを明示的に行います。 

const calculateGearRatio = function(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// the rest works the same
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

この例では、変数が明示的に代入されています。ポインタを指定しているため、function キーワードの後の関数名を省略できます。この唯一の難点は、呼び出される前に関数を宣言する必要があることです。 

関数式は特に、関数をオブジェクトのメンバーとして代入する場合にも使用されます。changeGear 関数を Bike.prototype に代入したときのことを覚えていますか?

Bike.prototype.changeGear = function(direction, changeBy) {
  if (direction === 'up') {
    this.currentGear += changeBy;
  } else {
    this.currentGear -= changeBy;
  }
}

関数を返す

関数は特権階級のオブジェクトであるため、関数を宣言するもう 1 つの方法は、関数が別の関数を返したときに行うことです。このパターンを通常はファクトリ関数といいます。 

// when invoked, this function will assign a function
function gearFactory(){
  return function(driverGear, drivenGear){
    return (driverGear / drivenGear);
  }
}
// calculateGearRatio can now be invoked as a function
const calculateGearRatio = gearFactory();
// and all the rest

上記の例はシンプルながら有効です。ファクトリ関数は、クロージャ内の変数参照をキャプチャする場合をはじめ、単発の再利用可能な関数で役立ちます。クロージャについては後の単元で説明します。 

無名関数

JavaScript には、機能させるために関数を渡す必要のある API が多数あります。たとえば、配列があり、その配列の値から派生した新しい配列を作成したいとします。この場合は、おそらく Array.map 関数を使用するものと思われます。

let myArray = [1, 5, 11, 17];
let newArray = myArray.map( function(item){ return item / 2 } );
console.log(newArray); // [0.5, 2.5, 5.5, 8.5]

このスニペットでは、myArray.map が 1 つのパラメータ、つまり、myArray の項目ごとに一回ずつ実行される関数を取ります。 

この関数は再利用できません。この関数に渡される引数 (名前はありません。だから「無名」です) として宣言され、map 関数の実装の内側で実行されます。無名関数 (一定の言語ではラムダともいいます) は JavaScript でごく普通に使用されています。 

関数の呼び出し

関数を宣言したら、おそらく呼び出してみたいと思うでしょう。関数が呼び出されると、いくつかのことが行われます。 

前述のとおり、最初に行われるのは、新しいフレームがスタックに入れられることです。次に、その変数と引数を含むオブジェクトがメモリ内に作成されます。続いて、this ポインタ がこのオブジェクトと他のいくつかの特殊オブジェクトにバインドされます。そして、引数に渡された値が代入され、最後にランタイムが関数のボディでステートメントを実行し始めます。 

this のバインドには、重要な例外が 1 つあります。この点については、非同期 JavaScript の単元で説明します。 

呼び出しと代入

関数を操作する場合に、JavaScript の入門者が混乱する原因となると思われるのが、関数を代入する/渡すのか、それとも呼び出すのかについてです。突き詰めれば、() を使用するかどうかの違いです。 

bike オブジェクトには calculateGearRatio 関数があります。 

let bike = {
  ...,
  calculateGearRatio: function() {
    let front = this.transmission.frontGearTeeth[this.frontGearIndex],
    rear = this.transmission.rearGearTeeth[this.rearGearIndex];
    return (front / rear);
  },
  ...
}

ここで、calculateGearRatio 関数への 2 通りのアクセス方法について考えてみます。

// invoke function and assign value to ratioResult
let ratioResult = bike.calculateGearRatio();
// assign calculateGearRatio function to a new pointer
const ratioFunction = bike.calculateGearRatio;

1 つ目の例では、calculateGearRatio が、ratioResult 変数に (この例ではプリミティブ値として) 代入されている関数から返された結果によって呼び出されています。他方、ratioFunction は、calculateGearRatio 関数に単純に代入またはポイントされています。逆にして、ratioFunction として呼び出すことも可能です。 

関数を別のポインタに代入するのには理由があります。特に Array.map() 関数などを使用する別の関数のパラメータとして代入する場合です。けれども、this 参照を使用する関数はすべて破損のリスクがあります。this が異なる時点で異なるものをポイントすることがあるためです。詳細は後で説明します。 

イベントハンドラとしての関数

関数をイベントの結果として起動したい場合は、関数をそのイベントに結び付ける必要があります。そうすれば、その関数がイベントハンドラになります。関数定義に、1 つの引数、つまり、関数を起動するイベントへのポインタを含める必要があります。 

var handleClick = function(event) {
}

各イベントには、対処するためにそのイベントについて知っておくべき内容を説明するプロパティがあります。たとえば、click の場合は、クリックに関するデータ (イベントの種別、どの要素によって起動されたのか、クリックの座標など) を確認できます。

var handleClick = function(event) {
  console.log(event.type);  // click
  console.log(event.currentTarget); // the thing you clicked
  console.log(event.screenX); // screen X coordinate
  console.log(event.screenY); // screen Y coordinate
}

DOM API を使用したイベントハンドラの代入

シンプルな Web ページでは、HTML に明示的に代入されたイベントハンドラを確認できることがあります。 

<button onclick="handleClick(event)">
  Click to Go
</button>

けれども、昨今の Web アプリケーションの HTML でイベントバインドが使用されていることはほとんどありません。その代わり、DOM API、特に JavaScript の Element.addEventListener() 関数が好んで使われています。 

この場合はまず、HTML 要素への参照が必要です。以下では、ボタンに id 属性を追加し、onclick 属性を削除しています。 

<button id=”clicker”>

いよいよ、DOM にアクセスして、ボタンへの参照を取得し、それを値として渡す方法で handleClick というイベントリスナーを代入します (括弧はありません)。 

let button = document.getElementById("clicker");
button.addEventListener("click", handleClick);

DOM API を使用すると、開発者が対話性やユーザの動作に対する応答性の高い UI を柔軟に作成できるようになります。また、機能をオフにする必要がある場合に、開発者がイベントリスナーを削除できます。 

button.removeEventListener("click", handleClick);

さらに、イベントリスナーとして追加された無名関数が表示されます。 

button.addEventListener("click", function(event){
  //...anonymous function body...
});

removeEventListener を使用して無名関数を削除することはできません。関数を識別するために渡すポインタがないためです。 

Lightning Web コンポーネントのイベントと関数

Lightning Web コンポーネントの主なコードアイテムは、JavaScript モジュール、HTML テンプレート、CSS ファイルです。このうち唯一必須であるのが JavaScript モジュールです (.xml ファイルも必須ですが、これはコードではなく、コンポーネントに関するメタデータです)。 

Lightning Web コンポーネントの関数は多くの場合、コンポーネントの JavaScript モジュールでエクスポートされたクラスのメンバーである、メソッドの形態になっています。これらの関数がイベントハンドラ、またはイベントハンドラから下方に呼び出される他の関数である場合があります。 

HTML テンプレートは、静的 HTML バインドと似た方法でハンドラ関数を参照しますが、実際には異なります。このテンプレートは JavaScript アーティファクトにコンパイルされるため、静的に見えるバインドも実際には、フレームワークがコンポーネントのライフサイクルのいずれかの時点で addEventListener を呼び出すために使用する単なる構文規則です。 

テンプレートのこのマークアップは、イベントハンドラのバインドを示しています。

<lightning-input onchange={handleChange} label="Input Text" value={text}>
</lightning-input>

これはイベントハンドラです。 

    handleChange(event){
        this.text = event.target.value;
    }
メモ

メモ

Lightning Web コンポーネントのイベントにはより高度な機能が多数あります。こうした機能を確認するには、[Lightning Web Components Basics (Lightning Web コンポーネントの基本)」モジュールを受講するか、いずれかのサンプルアプリケーションを実際に試してみてください。

リソース

JavaScript の関数

イベントハンドラ

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): Communicate with Events (イベントとの通信)