TL;DR
このページでは、アロー関数の実装方法について解説しますね。一言でいうとアロー関数とは、JavaScript(とその派生言語である TypeScript)における新しい関数の定義方法の一つで、従来の関数よりもコードがスッキリし、this
の扱いが容易になる特性があります。
開発環境 | バージョン |
---|---|
Next.js | 13.4.4 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
アロー関数とは?
アロー関数は、JavaScript ES6 から導入された新しい関数の定義方法で、特にコードがシンプルに書ける点と、this
の振る舞いが従来の関数とは異なる点が特徴として挙げられます。this
については後述します。
基本構文
従来の関数定義は以下のように記述されます。
function square(n) {
return n * n
}
一方、アロー関数では次のように表記できます。
const square = (n) => {
return n * n
}
これは更に省略して以下のように書くこともできます。
const square = (n) => n * n
それぞれの場合について、簡単なアロー関数の例を以下に示します。
引数がない場合
アロー関数に引数がない場合、引数リストを空の丸括弧 ()
で表現します。以下は、"Hello, World!"をコンソールに出力するアロー関数の例です。
const greet = () => {
console.log('Hello, World!')
}
greet() // "Hello, World!" を出力
引数が一つの場合
アロー関数がただ一つの引数を取る場合、その引数を囲む丸括弧を省略することが可能です。以下は、与えられた数値を 2 倍にして返すアロー関数の例です。
const double = (num) => {
return num * 2
}
console.log(double(3)) // 6 を出力
本体が一文である場合
アロー関数の本体が一つの文だけで構成されている場合、その文を波括弧 {}
で囲む必要はありません。さらに、その一文が表現式である場合、その結果は自動的にリターンされます。以下は、与えられた数値を 2 倍にして返すアロー関数の例ですが、この場合は本体が一文であるため波括弧と return
を省略しています。
const double = (num) => num * 2
console.log(double(3)) // 6 を出力
これらの例からわかるように、アロー関数はコードを簡潔に記述するための有用なツールです。ただし、その振る舞いは従来の関数とは異なるため、使用の際には注意が必要です。
アロー関数の様々な使い方
アロー関数の様々な使い方をご紹介します。
デフォルト値を持つパラメータ
JavaScript の関数では、パラメータにデフォルト値を設定することができます。アロー関数でも同じように設定することが可能です。次の例では、greet
関数の引数 name
にデフォルト値として 'World'
を設定しています。
const greet = (name = 'World') => `Hello, ${name}!`
console.log(greet()) // Hello, World!
console.log(greet('User')) // Hello, User!
可変長引数
JavaScript では、関数のパラメータに ...
を前置することで、任意の数の引数を配列として受け取ることができます。これを「可変長引数」と呼びます。アロー関数でも同様に可変長引数を使用することができます。
const sum = (...numbers: number[]) => numbers.reduce((acc, num) => acc + num, 0)
console.log(sum(1, 2, 3, 4, 5)) // 15
オブジェクトリテラルを返す
関数の戻り値としてオブジェクトリテラルを返す際、括弧()
で囲むことで正しくオブジェクトとして解釈させることができます。
const createPerson = (name: string, age: number) => ({ name, age })
console.log(createPerson('John', 30)) // { name: 'John', age: 30 }
高階関数(関数を返す関数)
関数の戻り値として別の関数を返すことも可能です。これは高階関数と呼ばれるパターンで、関数型プログラミングの一部としてよく利用されます。
const addPrefix = (prefix: string) => (name: string) => `${prefix} ${name}`
const greetMr = addPrefix('Mr.')
console.log(greetMr('John')) // Mr. John
即時実行関数
JavaScript では関数を定義と同時に実行することができます。これを「即時実行関数」と呼びます。アロー関数も即時実行することが可能です。
const result = ((num1: number, num2: number) => num1 * num2)(3, 4)
console.log(result) // 12
これらの特性も合わせて理解することで、アロー関数をより深く活用することができます。
this の振る舞い
このように、アロー関数はコードの見通しが良くなるだけでなく、this
の振る舞いが改善されるという特性も持っています。
従来の関数ではthis
は実行コンテクストに依存しますが、アロー関数では定義した時点のスコープのthis
を引き継ぎます。これにより、this
の振る舞いに頭を悩ますことが少なくなりました。
this
とは、JavaScript(および TypeScript)で使用されるキーワードで、オブジェクト自身を参照します。this
の値は、それが使用されるコンテクストにより異なります。例えば、以下のコードでは、this
はオブジェクトmyObject
を指します。
const myObject = {
property: 'Hello',
method: function () {
console.log(this.property)
},
}
myObject.method() // "Hello"を出力
この場合、this
はmyObject
を参照し、そのプロパティproperty
の値を出力します。しかし、関数やメソッドの中でthis
を使用すると、その振る舞いは関数がどのように呼び出されるかによって変わります。これは、特にコールバック関数やイベントハンドラなどで問題となることがあります。
この問題を解決するのがアロー関数です。アロー関数は、自身のthis
がないため、this
は周囲のスコープから引き継がれます。これは、それ自体が定義されたスコープのthis
を「記憶」するという特性を持ちます。この特性により、コールバック関数やイベントハンドラなどで問題となるthis
の振る舞いを簡単に制御することが可能になります。
TypeScript でのアロー関数
以下に TypeScript を用いたアロー関数の基本的な例をいくつかお見せします。
Interface を用いた例
TypeScript では、関数の引数や戻り値の型を定義するために Interface を用いることができます。以下の例では、Person
という Interface を定義し、Person
型の引数を取るアロー関数を作成しています。
interface Person {
name: string
age: number
}
const greet: (person: Person) => void = (person) => {
console.log(`Hello, my name is ${person.name} and I'm ${person.age} years old.`)
}
greet({ name: 'John', age: 25 }) // "Hello, my name is John and I'm 25 years old."
この例では、greet
関数の引数person
がPerson
型であることを確実にするために TypeScript の Interface を使用しています。
ジェネリクスを用いた例
TypeScript のジェネリクスは、型の再利用や抽象化を可能にします。以下の例では、ジェネリクスを用いてアロー関数を定義しています。
const identity: <T>(arg: T) => T = (arg) => arg
let output = identity<string>('myString') // type of output will be 'string'
この例では、identity
関数は任意の型T
を引数として受け取り、同じ型T
を返す関数として定義されています。このようにジェネリクスを用いることで、より汎用的なコードを書くことが可能になります。
アロー関数のメリット
アロー関数の他の特徴やメリットには以下のようなものがあります。
-
短縮構文: アロー関数は関数を宣言するための短縮構文を提供します。これにより、コードの冗長さが減り、可読性が向上します。
-
明示的なリターン: アロー関数の本体が波括弧(
{}
)で囲まれていない場合、その結果は自動的にリターンされます。これは、シンプルな関数やラムダ式を記述する際に便利です。 -
引数の省略: アロー関数がただ一つの引数を取る場合、その引数を囲む丸括弧を省略することが可能です。これにより、さらにコードのシンプルさが増します。
ただし、アロー関数はarguments
オブジェクトや、新たなインスタンスを生成するためのnew
キーワードとの互換性がありません。そのため、すべての場面で従来の関数を置き換えるものではないことを理解しておくことが重要です。
アロー関数を使うべきではないケース
アロー関数はその特性から、多くの場面で活用されますが、次のようなケースでは従来の関数を使うべきです。
メソッドの定義
オブジェクトのメソッドとして関数を定義する際には、アロー関数を使うとthis
が期待するオブジェクトを指さないため、従来の関数を使います。
const person = {
name: 'John',
greet: function () {
return `Hello, my name is ${this.name}`
},
}
console.log(person.greet()) // "Hello, my name is John"
この例では、greet
メソッド内のthis
がperson
オブジェクトを指します。もしアロー関数を使用した場合、this
はperson
オブジェクトではなく、外部のスコープを指してしまうため、意図した動作になりません。
イベントハンドラー
ブラウザのイベントハンドラーでは、アロー関数を使うとthis
が期待する要素を指さないため、従来の関数を使います。
document.querySelector('button').addEventListener('click', function () {
this.classList.toggle('active')
})
この例では、addEventListener
のコールバック関数でthis
を使っていて、this
はクリックされたボタン要素を指します。アロー関数を使用すると、this
はグローバルオブジェクトまたはundefined
を指すため、クリックされた要素にアクセスできません。
コンストラクター関数
new
キーワードと一緒に呼び出されるコンストラクター関数では、アロー関数を使うことはできません。アロー関数は自身のthis
を持たないため、新しいインスタンスを作成することができません。
function Person(name: string) {
this.name = name
}
const john = new Person('John')
この例では、Person
関数をnew
キーワードと共に呼び出して新しいオブジェクトを作成します。このとき、Person
関数内のthis
は新しく作成されたオブジェクトを指します。アロー関数を使うと、this
が新しいオブジェクトを指さないため、このようなパターンは不適切です。
アロー関数は便利であり、コードの簡潔性や可読性を向上させますが、上記のようなケースでは従来の関数が適切です。関数の選択は、それぞれの特性と利用するコンテキストによります。
Next.js で、アロー関数を実装
それでは Next.js と TypeScript のコードにおけるアロー関数の使用例を見てみましょう。この例では、Next.js のページコンポーネントの中でアロー関数を使っています。
/pages/index.tsx
の内容を示します。
import { NextPage } from 'next'
import React from 'react'
type Props = {
message: string
}
const IndexPage: NextPage<Props> = ({ message }) => {
const showMessage = () => {
alert(message)
}
return (
<div>
<button onClick={showMessage}>メッセージを表示</button>
</div>
)
}
IndexPage.getInitialProps = async () => {
const message = 'こんにちは、Next.js!'
return { message }
}
export default IndexPage
このコードでは、アロー関数を 2 つ使用しています。
1 つ目はconst IndexPage: NextPage<Props> = ({ message }) => {...}
の部分で、これはIndexPage
という名前の React コンポーネントをアロー関数を使って定義しています。このコンポーネントは、message
という名前の props を受け取ります。
2 つ目は、const showMessage = () => {...}
の部分です。この内部でアロー関数を定義し、その中でalert
を使ってメッセージを表示する処理を行っています。ここでもアロー関数を使っているため、シンプルで直感的な記述を実現しています。
高等技術としてのアロー関数の実装
次に、アロー関数を使ったより高度な実装例をご紹介します。
type ComplexProps = {
numbers: number[]
handleSum: (sum: number) => void
}
const ComplexComponent: React.FC<ComplexProps> = ({ numbers, handleSum }) => {
React.useEffect(() => {
const sum = numbers.reduce((a, b) => a + b, 0)
handleSum(sum)
}, [numbers, handleSum])
return <div>...</div>
}
この例では、アロー関数を 2 つ使用しています。1 つ目はconst ComplexComponent: React.FC<ComplexProps> = ({ numbers, handleSum }) => {...}
という部分で、ComplexComponent
という React コンポーネントをアロー関数を使って定義しています。
2 つ目はReact.useEffect
の中のconst sum = numbers.reduce((a, b) => a + b, 0);
という部分です。配列のreduce
メソッドを使い、各要素を加算しています。ここでもアロー関数が使われており、コールバック関数を簡潔に記述できています。
このように、アロー関数はコードをシンプルにし、可読性と保守性を高めるために重要なツールと言えます。