JavaScriptの関数を理解する
JavaScriptの関数を理解する
本記事について
要約
このモジュールでは、JavaScriptの関数についての深い理解を目指します。主な学習目標は、関数の短縮表記(アロー関数)の認識、キーワード「this」のスコープの理解、ES6+でのオプションパラメーター定義のメリットの説明、そして’…'演算子の使い方の理解です。また、this キーワードが引き起こす問題と、その解決策、関数パラメータの扱い方の改善についても触れています。
前提
Trailheadの英語版をChatGPT4(以下、ChatGPT)に投げて返ってきた内容を載せています。もともとはTrailheadのみで学習していたのですが、不自然な日本語や、JavaScript初学者に理解しづらい用語・概念を手っ取り早く調べるとためにChatGPTとやり取りしながらの学習に切り替えました。
ChatGPTに聞くとおおよそ同じような答えが返ってきますが、毎回聞くのも手間ですし、用語なども追加で聞いていることもあるので備忘録を兼ねて残します。
僕と同じようなJavaScript初学者の方に参考になれば幸いです。
対象のTrailhead↓
Understand JavaScript Functions 単元 | Salesforce Trailhead
学習目標
このユニットを完了すると、以下のことができるようになります。
- 関数のための太い矢印構文を認識する。
this
キーワードに使用されるスコープを説明する。- ES6+でオプションのパラメーターを定義することがなぜコードを綺麗にするのかを説明する。
- ‘
...
’ 演算子の異なる使い方を説明する。
Thisの問題
おそらく、以下のように関数を定義する方法は既に知っているでしょう。
let result = function (i,j) {
return i+j;
}
console.log(result(2,3));
このコードを実行すると、コンソールには5が表示されます。ES6は、アロー関数と呼ばれるものを使って関数を定義するより短い方法を導入しました。C#などの他の言語から来ている場合、アロー関数はラムダ式として知っているものに非常に似ているでしょう。太い矢印記号( =>
)を使って、同じ関数を以下のようなコードで作成することができます。
let result = (i,j) => i+j;
console.log(result(2,3));
ここで行ったのは、function
と return
キーワードを削除し、新しい太い矢印記号を代わりに使用しただけです。パラメーターが1つだけの場合は、括弧すらオプションで、式が1つ以上ある場合にのみ波括弧が必要です。ただし、波括弧を含む場合は、returnキーワードが必要であることを覚えておいてください。
アロー関数はコード量を減らし、特にネストした関数が関与するときに、this
キーワードを扱う際の混乱を減らします。関数は this
という特別な変数を持っており、これは関数を呼び出すために使用されるオブジェクトを指します。これは「動的なthis」とも呼ばれます。
this
の動的な性質は、特定の状況で問題を引き起こします。例えば、以下のコードでは、オブジェクトを使用して関数が呼び出されています。
let message = {
hello : 'Hello',
names : ['Sue', '
Joe'],
showMessage: function() {
this.names.forEach(function(name) {
console.log(this.hello + ' ' + name);
});
}
}
message.showMessage();
このコードをPlayCodeで実行すると、コンソールには「undefined Sue」と「undefined Joe」という2つのメッセージが表示されます。変数 hello
は、JavaScriptのインタープリターがそれを指定されていない関数の引数だと思っているため、ネストした関数の中からは参照できません。それはネストした関数の内部にスコープがないからです。ネストした関数の中で this
キーワードを参照すると、それはオブジェクトが呼び出されたスコープを指します。この場合、それはグローバルなので、hello
という変数は存在しません。
これを回避するために、showMessage
関数の中に self
という新しい変数を追加する以下のようなことができます。 self
変数は、「レキシカルスコープ」を参照します。これは showMessage
関数内で定義されたからです。
let message = {
hello : 'Hello',
names : ['Sue', 'Joe'],
showMessage: function() {
let self = this;
this.names.forEach(function(name) {
console.log(self.hello + ' ' + name);
});
}
}
message.showMessage();
このコードを実行すると、コンソールには「Hello Sue」と「Hello Joe」という正しいメッセージが表示されます。しかし、self
変数を使うことは回避策に過ぎません。
ES6が導入したアロー関数は、レキシカルスコープを組み込んでいます。ですので、上記のコードを以下のように置き換えることができ、これを表すために追加の変数を宣言する必要はありません。
let message = {
hello : 'Hello',
names : ['Sue', 'Joe'],
showMessage: function() {
this.names.forEach(name => {
console.log(this.hello + ' ' + name);
});
}
}
message.showMessage();
パラメーターの取り扱いの改善
ES6より前では、関数のパラメーターの取り扱いは面倒でした。コードが予想通りに動作するようにするためには、任意のパラメーターについて関数内で手動でチェックを追加することがよくありました。例えば、2つのパラメーターを持つ関数を考えてみてください。ユーザーが2つ目のパラメーターを入力しない
かもしれないので、そのチェックを行うコード行を関数に追加する必要があります。
function helloMessage (param1, param2) {
param2 = param2 || 'World';
return param1 + ' ' + param2;
}
console.log(helloMessage('Hello')); //Displays "Hello World"
ES6は、関数のパラメーターを扱うためのより良い方法を提供しています。今では、パラメーターリスト内で等号 ( =
) を使用してデフォルトのパラメーター値を指定することができます。 helloMessage
関数では、2番目のパラメーターは任意ですが、関数内部にその追加のコード行は必要ありません。
function helloMessage (param1, param2 = 'World') {
return param1 + ' ' + param2;
}
console.log(helloMessage('Hello')); //Displays "Hello World"
前のユニットで学んだオブジェクトのデストラクチャリング構文を利用して、名前付きパラメーターを模倣することさえできます。例えば、2つのパラメーターを持つこの関数を考えてみてください。
function showMessage(who, {p1 = "Hello", p2 = "World"} = {}) {
console.log(who + ' says ' + p1 + ' ' + p2);
}
showMessage("Trailhead"); //Displays "Trailhead says Hello World"
2番目のパラメーターは、デストラクチャリング構文で指定されたオブジェクトにすぎません。しかし、等号に続いて空の中括弧があることに注意してください。これにより、パラメーターなしで関数を呼び出すことが可能になります。そしてこれは重要です。なぜなら、等号がなければ、すべてのパラメーターを持たない最後の関数を実行しようとするとTypeErrorが発生するからです。
しかし、引数の数が未知の関数がある場合はどうでしょうか? ES5では、arguments変数を使うことができました。しかし、arguments変数はシンボルであり、配列ではなく、それを使うことは容易ではありませんでした。
ES6は、これらの残りの、ゼロ以上の、未知の引数にアクセスするためのより良い方法を導入しました。これをrest(残り)と言います。 Rest引数は三つのドット( ...
)で示され、引数リストの最後にしか現れません。例えば:
function showContact (firstName, lastName, ...titles) {
console.log(firstName
+ ' ' + lastName + ' - ' + titles.join(', '));
}
showContact("Joe", "Smith", "Developer", "Trailblazer", "Outdoorsman");
上記のコードでは、titlesパラメーターがrest引数です。 このパラメーターは配列であり、関数が受け取った未知のすべての追加パラメーターを保持します。