編集履歴
- 2025/03/12 以下のように内容修正
- アロー関数式 vs 一般関数の時点でなく、関数宣言・関数式・アロー関数式として分けて書き直す
- テーマの変更に合わせ、let/constの上書きなど、テーマと関連性が低い部分については略す
- 2025/03/07 記事作成
背景
オンライン診療サービスのWEBアプリケーションを開発するプロジェクトで働いている際に、
PRコードレビューを先輩エンジニアさんに投げたら
このような指摘がありました。
functionとして定義された関数は
const func = () =>のアロー関数式のように統一していただきたいです!
そちらの方がより良いコードの書き方が身につけるはずです。
当時には実務経験があまりなかったため、
関数を使う時にはほぼ関数宣言(Function Declaration)として使っていました。
その後、レビュー対応のためにアロー関数式でメソッドを書き直して提出し、
関数の使い方として「関数宣言(Function Declaration)」だけではなく「関数式(Function Expression)」があることも学べましたが、
今回に関数宣言・関数式についてより理解を深めたく本記事を書くようになりました。
関数(メソッド)の書き方
関数宣言(Function Declaration)
一般的に使われる関数・メソッド文法です。
function newFunc () {
console.log("test")
}
コードのフローの内部にて、このように「特定の関数を宣言する」という意味を持って
「関数宣言(Function Declaration)」になります。
関数式(Function Expression)
functionから始まるのではなく、式(Expression)の形で関数が定義されるときに「関数式」と呼びます。
この時にはfunctionの後ろに関数名を指定しなくても、指定した変数名として関数が使えます。
const newFunc = function (params) {
console.log("test", params)
}
関数式を書く時には、function ()の関数名は省略ができます。
ただ、MDNのドキュメントの引用通りでは、
関数名をつけることでデバッグや再帰関数も容易に使えるそうです。
However, a name can be provided with a function expression. Providing a name allows the function to refer to itself, and also makes it easier to identify the function in a debugger's stack traces:
もちろん、関数式の関数を呼び出す時には
関数が入っている変数名を使って呼び出すようになっています。
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1)
}
console.log(factorial(5)) //120
console.log(fac(5)) //Uncaught ReferenceError: fac is not defined
アロー関数式
ES6から紹介された文法として、
関数式をより簡潔に作成できる文法です。
const newFunc = (params) => {
console.log("test", params)
}
アロー関数は「関数式」に含まれるため、基本的には動きも関数式と似ていますが
細かいところで違いがあります。
場合によってより簡潔にも書ける
関数式内部のロジックがreturn 値
のみであれば、
{ return foo }
のようなロジックを () => ( foo )
の一行目で収めることができます。
const arrowFunc = (a,b) => (a+b)
const arrowFunc = (a,b) => {
return a+b
}
//簡潔バージョン
const SimpleArrowFunc = (a,b) => a+b
{}(curly brace)を()に変えてreturn値のみ書けばOKです。
数式系だったり、コードがより簡単であれば、()さえ省略できます。
ただ、目をおきまして、
Arrow Functionを使うときには
- {}のcurly braceではreturnを特に指定しなければ基本的にはvoid関数として扱われる(consoleで打てばundefined値を返していることがわかります)
- ()で紐付けばカッコの中にある値をreturnしますよーという機能があることを覚えておけばOK
などを覚えておきましたら問題なく使いこなせると思います。
このアロー関数はただのWEBサイトだけでなく、
WEBアプリケーションにもとても有用で、
Next.js・Reactなどのフレームワークのページコンポーネントに使われる.tsx(.jsx)形式のコンポーネントでも比較的に簡単なコードがかけます。
const menuDisplay = (isSP?: boolean) => (
<>
<MenuIcon />
<MenuItems isSP={isSP} />
</>
)
共通点
呼び出されるとメソッド内部のロジックを実行します。
主に関数宣言の場合は関数名を呼び出し、
関数式は関数が入っている変数名を呼び出してロジックを実行します。
相違点
一貫性のあるロジックを紐づいてロジックを実行させるというところでは大きな違いはありませんが、
動きに細かい違いがあります。
代表的にまとめますと、Hoisting、this、argumentsです。strict modeではない時には、パラメータの宣言も含まれます。
Hoisting
関数宣言
関数宣言の場合は、以下のようにHoistingができます。
一旦関数が宣言されていると、関数が定義される前にもアクセスができます。
funcSample() // arrowFunc called
function funcSample () {
console.log("arrowFunc called")
}
関数式・アロー関数式
関数式・アロー関数式両方Hoistingはできません。
関数式の場合は、関数式が定義されてから使えるようになります。
arrowFuncSample() //Uncaught ReferenceError: arrowFuncSample is not defined
const arrowFuncSmample = () => {
console.log("arrowFunc called")
}
アロー関数はもちろん、
var変数で定義された関数式もHoistingが使えません。
varの動き通りにfuncExpression変数を上段に上げましたが、まだundefinedとして認識していて、
関数式の宣言が行われていないため、関数のようには使えていない状態です。
console.log(varVal) //undefined
funcExpressionSample() //Uncaught TypeError: funcExpressionSample is not a function
var varVal
var funcExpressionSample = function () {
console.log("FuncExpression Called")
}
varをつけない場合にもReferenceErrorを出します。
funcExpressionSample() //Uncaught ReferenceError: funcExpressionSample is not defined
funcExpressionSample = function () {
console.log("FuncExpression Called")
}
関数名の表記
関数宣言
function()
から始まり、関数名も必須になります。
function (a,b) {
console.log(a*b)
}
//Uncaught SyntaxError: Function statements require a function name
function multiplyAB (a,b) {
console.log(a*b)
}
multiplyAB(5,6) //30
関数式(function()、アロー関数式)
指定した変数名で該当の関数が呼び出されるため、省略できます。
const plusValues = function (a,b) {
console.log(a+b)
}
const plusValuesByArrowFunc = (a,b) => {
console.log(a+b)
}
plusValues(3,4) //7
plusValuesByArrowFunc(3,4) //7
function関数式の場合は関数名がなくてもOKで、関数名を個別でつけることでもっとわかりやすく書いたり、内部で参照したりすることができます。
アロー関数の場合は特徴通りに関数名自体が持てません。(Anonymous Function)
const plusThreeValues = plusThree (a,b,c) => a+b+c
//Uncaught SyntaxError: Malformed arrow function parameter list
引数(パラメータ、arguments)について
同じパラメータの宣言
普通には発生しないことですが、
関数宣言・関数式・アロー関数式それぞれ違い動きを見せていました。
function構文(関数宣言・関数式)
function duplicatedParams (a,b,a) {
console.log(a+b)
}
duplicatedParams(1,2,3)// aが3に上書きされ, 3 + 2 = 5
const duplicatedParamsFunctionExpress = function(a,b,a) {
console.log(a + b)
}
duplicatedParamsFunctionExpress(1,2,3)// aが3に上書きされ, 3 + 2 = 5
パラメータaが2回定義されている状態ですが、
後に定義されたaパラメータ(3番目のaパラメータ)が
1番目のaパラメータを上書きします。
function()を使っている時にはご覧のように同じパラメータとして定義しても
エラーを出さず、パラメータを上書きします。
アロー関数式
const duplicatedParamArrowFunc = (a,b,a) => {
console.log(a + b)
}
duplicatedParamArrowFunc(1,2,3) //Uncaught SyntaxError: Duplicate parameter name not allowed in this context
アロー関数式の場合には、
同じパラメータを連続で定義できません。
use strict
ただ、functionを使った関数だとしても、
use strictがついていればこのように同じパラメータを宣言することはできなくなります。
"use strict"
function duplicatedParams (a,b,a) {
console.log(a+b)
}
duplicatedParams(1,2,3)// Uncaught SyntaxError: Duplicate parameter name not allowed in this context
const duplicatedParamsFunctionExpress = function(a,b,a) {
console.log(a + b)
}
duplicatedParamsFunctionExpress(1,2,3)// Uncaught SyntaxError: Duplicate parameter name not allowed in this context
argumentsの使用
関数に渡された実引数の値を含めているargumentsについても、以下のような結果が得られました。
- function()系は使える
- アロー関数式は使えない
(argumentsに関してはMDN公式ドキュメントにも載っています。)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Function(関数宣言・関数式)
function showArgumentsByFunc (a,b) {
console.log(a+b)
}
showArgumentsByFunc(3,3) //Arguments(2) [3, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
const showArgumentsByFunctionExpression = function() {
console.log(arguments)
}
showArgumentsByFunctionExpression(1,2,3) //Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
function()系は、argumentsがパラメータとして定義されていなくても類似配列としてアクセスできるようになっています。
アロー関数
argumentsがないため、アクセスできません。
const showArgumentsByArrowFunction = () => {
console.log(arguments)
}
showArgumentsByArrowFunction(1,4,7)
//Uncaught ReferenceError: arguments is not defined at showArgumentsByArrowFunction
ご覧のように、アロー関数式はargumentsが定義されていないため、エラーが出るようになります。
thisの使用
argumentsと同じです。
function()系は参照可能、アロー関数式は参照不可です。
Function(関数宣言・関数式)
const normalObj = {
name: "Ethel",
cry: "OiiaOiia",
test: function() { console.log(this.name + " cries " + this.cry) }
}
normalObj.test() //Ethel cries OiiaOiia
アロー関数
const arrowObj = {
name: "Ethel",
cry: "OiiaOiia",
test: ()=>console.log(this.name + " cries " + this.cry)
}
arrowObj.test() // cries undefined
その他
アロー関数はbinding値を持っていないため、
this、arguments、super、 _prototype_
などが使えなく、
new Date()のようなConstructorとしても使えません。
よくは見えないコードですが、yieldも使えません。
function()使用の関数はthisを指定したら自分が呼ばれたオブジェクトを従いますが、
アロー関数式はbindingが指定されておらず、window・document・global object・globalthisなどを従います。
アロー関数式はConstructorとしても使えず、_prototype_
もありません。
const Foo = () => {}
const foo = new Foo() // TypeError: Foo is not a constructor
console.log("prototype" in Foo) // false
なので、以下の場合はアロー関数式を使うのは不利になり得ます。
- Constructorが必要
- this値を使う必要がある
相違点まとめ
分類 | 関数宣言 | 関数式 | アロー関数式 |
---|---|---|---|
Hoisting | 🟢 | ❌ | ❌ |
関数名の必要の有無 | 必須 | 任意 | 無 |
this | 🟢 | 🟢 | ❌ |
arguments | 🟢 | 🟢 | ❌ |
同一名のパラメータ定義 | 🟢1 | 🟢1 | ❌ |
Constructor | 🟢 | 🟢 | ❌ |
何を使うべきか
より安全なコードを書くなら関数式・アロー関数式に有利な面が一部あると思いますが、
その頃にコードレビューを受けた後・本記事を書き直した後考察をしたら以下のように思います。
- 詳しくは個人の好みやチーム開発のルールに従う
- ただし、この動き上の特徴を覚えておいて、理解度を持って実装することこそ大事
筆者の私は、予測できなかったHoistingとか上書きが防げる関数式、
特にアロー関数式を使ってロジックを書きます。
ただし、
関数式・アロー関数式の場合はHoistingができないということもあり、
特にアロー関数式にはthis・Constructorなどの機能が使えないことを覚えて書く必要があると思いました。
一旦、
AirBnBのJSオープンソースでは、Hoisting防止のためコードルールとして関数式を推奨していますが、
「関数表現式の方が絶対的に有利」よりは「場合によって適切な関数の使い方があるはず」だと思います。
まとめ
- 関数宣言(Function Declaration)・関数式(Function Expression)・アロー関数式(Arrow Function)三つとも、内部のロジックを回すという共通点はあるが、細かい違いがある
- その違いには、関数式や関数宣言一つの方が絶対的に有利・不利だということを示すわけではなく、場合によって適切な使い方があるということを示している
- そのため、チーム開発の場合PRのルールを従いつつ、関数の定義のやり方について理解・考察してコードを作成していくことこそ大事
参照