はじめに
今回は、初心者から中級者になるためのJavaScriptを学んでいきましょう。
JavaScriptを全く勉強したことがないという人には以下の記事がおすすめです。
【JavaScriptの超基本】ファイルのインポートやエクスポートについて簡単に解説
【JavaScriptの超基本】コールバック関数について簡単に解説
【JavaScript】プリミティブ型とオブジェクト型を理解したい
それでは、頑張っていきましょう。
配列やオブジェクトのちょっと便利な構文
それではJavaScriptのちょっと便利に構文について学んでいきましょう。
オブジェクトのkeyとvalueに変数を指定
JavaScriptではオブジェクトのkeyとvalueに変数を指定することができます。以下のコードです。
const keyName = "bar";
const baz = 3
const obj = {foo: 1, [keyName]: 2, baz: baz};
console.log(obj)
{ foo: 1, bar: 2, baz: 3 }
上記の例のように、オブジェクトのkeyに変数を指定するときは[]
で囲い、valueに変数を指定するときはそのまま代入します。
keyName
の値であるbar
がオブジェクトのkeyに、baz
の値である3
がオブジェクトのvalueになっていることが分かると思います。
プロパティ名のショートハンド
次は、プロパティ名のショートハンド
と呼ばれる書き方です。
変数を{}
で囲うことで、変数名をkey・値をvalueに持つオブジェクトを生成することができます。
const organization = 'unreact';
const obj = {organization};
console.log(obj);
{ organization: 'unreact' }
プロパティ名のショートハンドという書き方はES2015から導入された構文で、React開発においても頻繁に使用されます。ぜひともマスターしておいて下さい。
配列の分割代入
他の言語でも頻繁に使われますが、JavaScriptにおいても配列の分割代入は可能です。
配列の分割代入で値を受け取る際は、受け取る側も配列にしておきます。
const [n, m] = [1, 2];
console.log(n, m);
1 2
上記の例では、n
に配列の1つ目の要素である1
が代入され、m
に配列の2つ目の要素である2
が代入されます。
JavaScriptの配列には順番が存在することを考えれば、値を受け取る側の順番が、値を与える側の順番に対応するというのは当然の挙動ですよね。
オブジェクトの分割代入
当然ですが、オブジェクトの分割代入も可能です。
const onj = {organization: 'unrect', age: 1};
const {organization, age} = onj;
console.log(organization, age);
unrect 1
オブジェクトの分割代入においては、受け取る側に{}
を準備して、その中にオブジェクトのkey名を書きます。
そのkey名に対して、オブジェクトの中で対応するvalueがそれぞれ格納されていきます。
つまり上記の例では、organaization
というオブジェクトのkeyに対応するunreact
というvalueが、受け取る側の{}
の中のkey名であるorganaizationに代入されることになります。
配列と違いオブジェクトには順番が存在しないため、分割代入においてはkey名で指定することが必要になります。
上記の例では、オブジェクトの分割代入を行った際の変数名が、オブジェクトのkey名になってしまっています。
以下のようにすることで、分割代入で生成する変数名を任意の名前にすることができます。
const onj = {organization: 'unrect', age: 1};
const {organization: group, age} = onj;
console.log(group, age);
unrect 1
上記の例では、organization: group
の部分で、オブジェクトのorganaization
に対応するvalueであるunreact
を、group
という変数名で受け取っています。
また以下のようにすれば変数名に初期値を割り当てることができます。
const onj = {age: 1};
const {organization: group = 'unreact', age} = onj;
console.log(group, age);
上記の例では、オブジェクトにはorganaization
が存在しないため何もしなければgroupにはundefined
が格納されます。
それを防ぐために、='unreact'
とすることで初期値を設定しています。
オブジェクトの分割代入はReactでpropsを受け取る際などに頻繁に利用されます。ぜひとも覚えておきましょう。
スプレッド構文
それでは次にスプレッド構文をまとめていきます。スプレッド構文とは...
をつけることでオブジェクトや配列の中身を展開する構文です。
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log(arr2);
const obj1 = {organaization: 'unreact'};
const obj2 = {...obj1, age: 1};
console.log(obj2);
[ 1, 2, 3, 4, 5, 6 ]
{ organaization: 'unreact', age: 1 }
配列のスプレッド構文はES2015から導入され、オブジェクトのスプレッド構文はES2018で導入されました。
使用例としては、オブジェクトや配列をコピーする際などに用いられます。
具体例を解説する前に、値渡しと参照渡し(のようなもの)について学んでおきましょう。
参照渡し(のようなもの)
配列やオブジェクトをそのまま代入すると値渡しではなく参照渡し(のようなもの)が行われます。
どういうことか分からないと思うので、具体例で解説します。以下が参照渡し(のようなもの)
が行われる例です。
const arr1 = [1, 2, 3];
const arr2 = arr1;
arr2.push(4);
console.log(arr1);
[ 1, 2, 3, 4 ]
上記の例のように、arr2.push(4)
を行ってarr2
に対して変更を行うと、arr1
にも変更が加えられます。
なぜこのような挙動をするのかというと、オブジェクトや配列は参照渡し(のようなもの)
が行われるからです。
正確には、JavaScriptには参照渡しは存在せず、全ての値に値の参照が渡されます。それは、プリミティブでもオブジェクトでも代わりはありません。ただ、変数aにプリミティブ値1を渡し、変数bに変数aを代入した後に、変数bにプリミティブ値10を渡しても、変数aにはプリミティブ値1が入っているという挙動が発生するのですが、これは変数bに格納されている参照がプリミティブ値10に置き換わっただけです。これは値渡しのように見えますが、ただ格納されている参照が移し変わっただけというわけです。
一方、オブジェクトのプロパティアクセスにより変数の再代入を行った場合、代入を行った対象が変数自体ではなく、変数に格納されているオブジェクトの参照に対して行われるため、参照渡しのような挙動が発生します。
値渡し
上記の参照渡しを回避するためには、arr2
のために新たに配列のメモリ領域を確保する必要があります。
そのために、下記のようにスプレッド構文を用いることがよくあります。
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
arr2.push(4);
console.log(arr1);
[ 1, 2, 3 ]
このようにすれば、値をコピーして渡すことができます。const arr2 = [...arr1]
の部分で新たに配列を生成し、そのメモリ領域を確保した後に、その新たに確保したメモリ領域の参照をarr2
に渡しています。
シャローコピーについて
先程配列をスプレッド構文でコピーして値渡しを行う方法について解説しましたが、この方法は少し問題を抱えています。
というのも、スプレッド構文による値渡しは、一段回までの深さしか行えないからです。
つまり、オブジェクトや配列がネストされていた場合は、そのネストされたオブジェクトや配列に対しては参照渡しを行ってしまいます。
以下の具体例で見てみましょう。
const obj1 = {
organaization: "unreact",
nest: { group: "react" },
};
const obj2 = {...obj1};
obj2.nest.group = 'React';
console.log(obj1);
{ organaization: 'unreact', nest: { group: 'React' } }
上記の例では、const obj2 = {...obj1}
の部分でスプレッド構文による値渡しを行っていますが、ネストされたオブジェクトである{ group: "react" }
に関しては参照渡しが行われてしまいます。
そのため、obj2.nest.group = 'React'
を行いobj2に対してネストされたオブジェクトの値を変更すると、obj1のネストされたオブジェクトの値まで書き換えられてしまいます。
このシャローコピーを回避するために、いくつかの方法があります。
最も有名なのはJson.parse/stringfy
を使う方法でしょうか。
ディープコピーを行う
それでは、このシャローコピーを回避してディープコピーを行う方法について解説していきます。
最も有名なのは、以下のようにJson.parse/stringfyを行う方法です。
const obj1 = {
organaization: "unreact",
nest: { group: "react" },
};
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.nest.group = "React";
console.log(obj1);
{ organaization: 'unreact', nest: { group: 'react' } }
JSON.parse(JSON.stringify(obj1))
の部分で、オブジェクトを一度JSON文字列に変換した後、再びJavaScriptのオブジェクトに変換しています。
この方法はお手軽で高速にディープコピーを行うことができますが、強引であるがゆえに弊害もあります。
下記の記事に、弊害について分かりやすくまとめてありました。
JavaScriptのDeepCopyでJSON.parse/stringifyを使ってはいけない
かいつまんで説明すると、この方法はオブジェクトのプロパティにDate
や関数
、undefined
が存在する場合に上手く動きません。
このような問題があるため、現環境でディープコピーを行う際はLodash
というライブラリのcloneDeep()
を用いる方法が推奨されています。
Lodashでディープコピー
Lodashを使えば簡単にディープコピーを行うことができます。
import _ from "lodash";
const obj1 = {
organaization: "unreact",
nest: { group: "react" },
};
const obj2 = _.cloneDeep(obj1);
obj2.nest.group = "React";
console.log(obj1);
{ organaization: 'unreact', nest: { group: 'react' } }
const obj2 = _.cloneDeep(obj1);
の部分でディープコピーを行っています。凄く簡単ですよね。
これからディープコピーを使用する際は積極的に使っていきましょう。
##関数型プログラミングっぽく書こう
JavaScriptはかなり関数型プログラミングのパラダイムに対応しています。
関数型プログラミングとは、先行する式の評価を後続の式に適用し、それをつなげていくことで最終的な評価値を得るプログラミング手法です。
ifやfor文による制御構造による手続き書き連ねていく命令型プログラミングのパラダイムとは異なり、関数型プログラミングはy = f(x)のような数学的な式であるべき状態を宣言
します。
React開発ではこの関数型プログラミングのような書き方が非常に好まれるため、積極的に使用していく必要があります。例えば、ReactのJSXの中では文
を使用することができず、全て式
を用いてプログラムを記述する必要があります。
ifやforのような文
ではなく、値を返す式
を組み合わせてあるべき状態を宣言していく、そんな宣言的(Declarative)
な書き方を学んでいくことで、少しレベルアップできるはずです。
それでは頑張っていきましょう。
ショートサーキット評価
最初にショートサーキット評価
、またの名を短絡評価
についてまとめていきます。
短絡評価とは&&
や||
などの論理演算子が左から右に評価される性質を利用して、右辺が評価されるかどうかを左辺に委ねる方法です。
&&
を使用した場合、左辺がtruthyのとき右辺を返し、||
を使用した場合は左辺がfalsy
なときに右辺を返します。
以下で具体例を見ておきましょう。
const organaization = "" || "UnReact";
console.log(organaization);
UnReact
上記の例では、 "" || "UnReact"
の部分でショートサーキット評価を行っています。
||
は左辺がfalsyのときに右辺を返し、左辺がtruthyのときは左辺を返す(短絡する)式です。
空文字はfalsyな値のため、右辺が返ってきてorganaization
に代入されています。
ちなみに、JavaScriptにおいてfalsyな値はfalse
、0
、NaN
、null
、undefined
、''(空文字)
のみで、それ以外は全てtruthyな値になります。
以下の例のように、ショートサーキット評価は繋げて書くことができます。
const organaization = 'Uniqlo' && 'Softbank' && 'Toyota' && 'UnReact';
console.log(organaization);
UnReact
&&
は値が左辺がtruthyなとき右辺に評価を委ねる記法です。そのため、上記の例では一番右側まで評価されてUnReact
が返ってきます。
このショートサーキット評価はif文の代わりに使うことができます。
ifなどの文
と異なり、値を返す式
なのでReact開発で頻繁に利用されます。
###Nullish Coalescing
次はNullish Coalecing、通称Null合体演算子をやっていきましょう。
Nulllish Coalescingはショットサーキット評価の||
と似ています。||
は、左辺がfalsyな値のときに右辺を評価するという記法ですが、Nullish Coalescingは??
を用いて左辺がnullまたはundefinedのときに右辺を評価するという記法です。
具体例は以下になります。
const organaization = null ?? "UnReact";
const language = undefined ?? "React";
console.log(organaization);
console.log(language);
UnReact
React
このように??
を用いて、左辺がnullまたはundefinedのときに右辺を評価するという記法がNullish Coalescingです。
ちなみに、上記のパターンだとNullish Coalescingの??
の部分をショートサーキット評価の||
に変えても特段違いはありません。
しかし、ショートサーキット評価の||
は0や空文字が左辺に入った際も右辺を評価してしまうため、思わぬバグを生む可能性があります。
そのため、nullまたはundefinedのときに右辺を評価するということを明示的に示したい場合にNullish Coalescingを使用することになります。
ちなみにNullsish CoalescingはES2020で追加されたものなので、Node.js 14.0以降で実行することになります。
Optional Chaining
それでは、Optional Chainingについて解説していきます。
こちらもES2020から追加された記法で、端的に書くならオブジェクトに対するプロパティアクセスを用いた際に、各参照が正しいかどうかを明示的に確認せずアクセスすることができるもの
です。
もしも途中のプロパティが存在していなかったら、式が短絡してundefined
が返ってきます。
この説明では何がなんだか分からないと思うので、きちんと順を追って解説していきます。
まず、前提としてオブジェクトの存在しない要素に対してプロパティアクセスを行うとundefined
が返ってきます。
const obj = { organization: "UnReact" };
console.log(obj.employee);
undefined
そして、この返ってきたundefinedに対してプロパティアクセスを行うとエラーが発生します。
const obj = { organization: "UnReact" };
console.log(obj.employee.age);
at ModuleJob.run (internal/modules/esm/module_job.js:110:37)
at async Loader.import (internal/modules/esm/loader.js:179:24)
この前提を踏まえた上で、次のようなケースを考えます。
const arr = [
{ organaization: "UnReact", employee: { name: "poocomaru", age: 23 } },
{ organaization: "Softbank", employee: null },
];
arr.map((n) => {
console.log(n.employee.age);
});
at ModuleJob.run (internal/modules/esm/module_job.js:110:37)
at async Loader.import (internal/modules/esm/loader.js:179:24)
上記のコードでは、会社についての情報を格納したオブジェクトの配列を作成して、それをmapで回しながらプロパティにアクセスしています。
このコードは、2つ目のオブジェクトに対してのn.employee.age
を行う部分でエラーが発生してしまいます。
というのも、n.employee
の実行結果がnull
であり、そのnullに対してプロパティアクセスを行ってしまったために発生するエラーです。
Optional Chainingの実装以前はifによる条件分岐でnullやundefinedチェックを行い、エラーを回避していました。
しかし、Optional Chainingを使用すれば、以下のコードのように簡単にエラーを回避することができます。
const arr = [
{ organaization: "UnReact", employee: { name: "poocomaru", age: 23 } },
{ organaization: "Softbank", employee: null },
];
arr.map((n) => {
console.log(n?.employee?.age);
});
23
undefined
変更が加わったのはn?.employee?.age
の部分です。通常のプロパティアクセス.
から、Optional Chainingである?.
に変更しています。
この変化により、nullに対してのプロパティアクセスが短絡されてundefined
が返ってくるようになり、nullに対してプロパティアクセスを行ったが故のエラーが発生しなくなります。
終わりに
今回はちょっと高度なJavaScriptの文法について取り扱いました。
少しでも皆様のお役に立てれば幸いです。
ここまで読んで頂きありがとうございました。