#97 ES6 基本から
まずはモダンJS ES2015(ES6)
let : 変数の有効範囲が{}(ブロックスコープ)に限定する
if (true) {
let x = 13;
}
console.log(x);
結果は、ReferenceError: x is not defined と参照できないエラーになる
{}を外れた時点で、変数xは破棄される。
let : 同名の変数は定義できない
let msg = 'Hello';
let msg = 'Good Bye';
console.log(msg);
SyntaxError: Identifier 'msg' has already been declared と、その識別子msgはすでに宣言されているから構文エラーです。とエラーになる。
const : 定数を宣言するための宣言
const author = 'YAMADA.Yoshihiro';
author = 'WINGS Project';
console.log(author);
TypeError: Assignment to constant variable. と、型エラー:定数の指定とでる。constant variableが変数という意味。
const list = ['React', 'Vue', 'Angular'];
list[0] = 'Riot';
console.log(list[0]);
const : 再代入はできないけど、変更はできるので、これはエラーにならない。
const list = ['React', 'Vue', 'Angular'];
list = ['Java', 'C#', 'Python'];
console.log(list[0]);
これはlistに対して、再代入しているので、TypeError: Assignment to constant variable. と、型エラー:定数の指定とでる。
letかconstか。一般的にはconstを使う。アプリの場合は、元の値を上書きしなければならない状況はさほどない。あとから値を変更したくなったら、その時点でletに置き換えても遅くない。
リテラル : 値の方。文字列リテラル、数値リテラル、関数リテラル、オブジェクトリテラル
テンプレート文字列
const fullname = 'Taro Suzuki';
const msg = `Hello, ${fullname}
How are you today?`;
console.log(msg);
答えは、
Hello, Taro Suzuki
How are you today?
バッククォートで囲むと改行も変数も文字列と一緒に書ける。変数は文字列と区別するために${}で囲む。
昔は改行を\nにして、変数は+で追加しないといけなかった。
タグ付きテンプレート文字列 css`…`など
アロー関数
const circle = (radius)=>{
return (radius ** 2) * Math.PI;
}
console.log(circle(10));
引数と関数を=>(アロー)でつなぐから、アロー関数と呼ばれる
例は、**はES2016で策定されたべき乗演算子。累乗ができる。
(radius ** 2)で、radiusの2乗を表す。これは半径*半径*Math.PI(3.14)で、円の面積を求めている。
昔の書き方だと、こんな感じ。
function circle(radius){
return (radius ** 2) * Math.PI;
}
アロー関数 : 一文だと{}を省略できる 値がそのまま関数の戻り血なのでreturn文も削除
const circle = (radius)=> (radius ** 2) * Math.PI;
console.log(circle(10));
アロー関数:引数が1個だと()も削除。引数がない時は()は省略できない
const circle = radius=> (radius ** 2) * Math.PI;
console.log(circle(10));
const show = () => console.log('Hello');
オブジェクトリテラルの簡易構文 変数と同名の簡易構文
プロパティと値を格納した変数の名前が同じなら、値の指定を省略できる
省略していないバージョンはコメント部分
const title = '速習React';
const price = 500;
const book = { title, price };
// const book = { title: title, price: price };
console.log(book);
console.log(book.title);
console.log(book.price);
コンソールには、オブジェクトはオブジェクト、値は格納された値が表示される。
オブジェクトリテラルの簡易構文 メソッド
ES5までは、メソッドもプロパティ名:関数と表していたけど、ES6からはメソッド(){…}のように表示できるようになった。プロパティとの違いが明示的に表せるので積極的に使うといい
// const member = {
// name: 'ほげほげ',
// greet: function() {
// console.log(`こんにちは、${this.name}さん!`);
// }
// }
const member = {
name: 'ほげほげ',
greet() {
console.log(`こんにちは、${this.name}さん!`);
}
}
member.greet();
コンソールには、こんにちは、ほげほげさんと表示される
算出プロパティ名 プロパティ名を動的に生成する
プロパティ名をブラケットで括ることで、式の値からプロパティ名を生成できる
let i = 0;
const member = {
[`attr${++i}`]: 'ほげほげ',
[`attr${++i}`]: '人間',
[`attr${++i}`]: '100歳'
};
console.log(member);
console.log(member.attr1);
console.log(member.attr2);
console.log(member.attr3);
コンソールには ほげほげ 人間 100歳と表示される
分割代入 配列の場合
配列から特定の値を抜き出して、個別の変数に割り当てる構文。
const list = [10, 20, 30];
const [x, y, z] = list;
console.log(x, y, z);
const [a, b] = list;
console.log(a, b);
const [l, m, n, o] = list;
console.log(l, m, n, o);
const [p, , r] = list;
console.log(p, r);
配列 [10, 20, 30]を変数 x, y, zに割り当てたり
配列の前から2個分 a, b に割り当てたり
配列の値の数より、変数の方が多く指定されていたら、変数に格納できないのでundefinedが返ってくる。
カンマで区切ることで、配列の値を飛ばし飛ばし変数に格納することもできる
もしも、配列を何個の変数に割り当てていいかわからない時は、残りの値をまとめて、スプレッド演算子(…演算子)を使える
const list = [10, 20, 30];
const [one, ...rest] = list;
console.log(one, rest);
console.log(list);
console.log(...list);
…restとした時は、10 [20, 30]とまとめて部分配列に入る
listだと[10,20,30] 配列のまま表示される
…listだと 配列の意味が消えてただの中身が表示される
スプレッド演算子はES2018にリリースされた
参考:
分割代入 オブジェクトの場合
基本的には、配列の分割代入と同じ書き方だけど、大括弧じゃなくて中括弧で囲む
const member = {
fullname: 'ほげほげ',
sex: 'man',
age: 100
};
const { fullname, sex, memo = '---' } = member;
// let fullname, sex, memo;
// ({ fullname, sex, memo = '---' } = member);
console.log(sex, fullname, memo);
オブジェクトにはキーがあるから、配列と違って、番号を気にせずに書ける(キーと変数が同名なら)。割り当ててないプロパティがあっても大丈夫。この場合はage。
また目的のプロパティがない場合に備えて、「変数名=デフォルト値」の形式で既定値を設定することも可能。
キーと変数名を違う名前にするなら、下記のように代入する
const member = {
fullname: 'ほげほげ',
sex: 'man',
age: 100
};
const { gender = sex } = member;
console.log(gender);
配列と同様に残りのプロパティを取得するなら、残りの値をまとめて、スプレッド演算子(…演算子)を使って代入する
const member = {
fullname: 'ほげほげ',
sex: 'man',
age: 100
};
const { age, ...rest } = member;
console.log(age);
console.log(rest);
コンソールには
100
{ fullname: 'ほげほげ', sex: 'man' }
と表示される
入れ子のオブジェクトの分解
入れ子のオブジェクトを展開するならば、代入先の変数も{…}を入れ子にします。
const member = {
fullname: 'ほげほげ',
address: {
prefecture: '東京都',
city: '西区'
}
};
const { address, address: { city } } = member;
console.log(address);
console.log(city);
分割代入側も、{}で入れ子にして指定する
コンソールログには、console.log(address);では、addressプロパティの値そのもの(オブジェクト)がそのままとりだされるだけ。
もし、変数に代入していない場合は、入れ子の場合は、
console.log(member.address.city); となるので冗長になりやすい。わかりやすいけど。
配列でも同様に、入れ子の値を分割代入できる
const list = [200, [300, 301, 302]];
const [x, [y1, y2, y3]] = list;
console.log(y1, y2, y3);
コンソールには300 301 302と表示される
宣言文と代入文を切り離した場合は注意が必要
let fullname, sex, memo;
({ fullname, sex, memo = '---' } = member);
console.log(fullname);
代入文の外側を()で括らないと、左辺の{}がブロックと見なされてしまう。。
同様の理由で、オブジェクトリテラルを返すだけの関数を作る場合は、右辺のブラケットの外側を()で囲わないとundefinedとエラーとなる
const func () => ({title:'hoge'});
分割代入によるオブジェクト引数の分解
function greet({ name, age }) { //分割代入による引数
console.log(`こんにちは、私は${name}、${age}歳です。`);
}
const my = { name: '佐藤理央', sex: 'female', age: 18 };
greet(my); //メソッドは実行
オブジェクトを引数にすると、テンプレート文字列の中で、簡単に呼び出せる。Reactアプリのpropsの受け取りでよく使う。
const greet2 = (obj) => {
console.log(`こんにちは、私は${obj.name}、${obj.age}歳です。`);
}
const my = { name: '佐藤理央', sex: 'female', age: 18 };
greet2(my);
オブジェクトをつけないと、テンプレート文字列の中で、仮引数をキーとして付ける必要があるので少し冗長になる。
仮引数にデフォルト値を設定できる
function getTrapezoidArea(upper = 1, lower = 1, height = 1) {
return (upper + lower) * height / 2;
}
console.log(getTrapezoidArea(10, 5, 3));
console.log(getTrapezoidArea(10, 5))
console.log(getTrapezoidArea(10));
console.log(getTrapezoidArea(1, 1, 1));
console.log(getTrapezoidArea());
台形の面積を求める関数。3つの仮引数にデフォルト値が入っているので、引数を指定しないで実行すると
console.log(getTrapezoidArea()); (1+1)*1/2=で 1が返る
console.log(getTrapezoidArea(10)); だと1つ目の仮引数に10が入り、後の2つは1なので、(10+1)*1/2=5.5 が返る
既定値には式を渡すことも可能
function getTrapezoidArea(upper = 1, lower = upper, height = upper) {...}
可変長引数の関数
任意個数の実引数でも、仮引数に…をつけることでまとめて配列として取得できる
function sum(...nums) {
let result = 0;
for (const num of nums) {
result += num;
}
return result;
}
console.log(sum(10, 25, 2));
console.log(sum(7, 13, 25, 6, 100));
任意個数の実引数分を…演算子付きの仮引数で配列として受け取って、for of 文で回して全部足し終わったら、結果を返す処理
for of文もES6から登場。
配列ならfor of 、オブジェクトならfor inで回す
インデックスも取りたいならArrayのメソッドのforEach()を使う
とりあえず、配列なら for of と。
仮引数が複数あって、可変長な仮引数も含む場合は、引数リストの末尾におく
function hoge(title, ...args){ ... }
実引数に…をつけると、まとめずにバラバラにするスプレッド構文になる
function sum(...nums) {
let result = 0;
for (const num of nums) {
result += num;
}
return result;
}
console.log(sum([10, 25, 2]));
console.log(sum(...[10, 25, 2]));
console.log(sum([10, 25, 2])); だと配列そのままが配列として…numsに入るので、正しい結果が得られない
console.log(sum(...[10, 25, 2])); のようにスプレッド演算子をつけると、実引数がバラバラになり、…numsで可変長の配列としてまとめられて、正しい結果が得られる
Optional Chaining演算子(?.)
たとえば、文字列を切り出すsubstringメソッドの場合
const str = 'Mozilla';
console.log(str.substring(1));
コンソールには ozilla が出る。
ただ、str(メソッドを呼び出すレシーバー)に、この時はMozillaが入っているけど、代入を忘れている時は、nullやundefinedの時がある。
ちなみにnullはドイツ語でゼロ。何もないという意味
たとえば、下記のコードはコンソールに、
TypeError: Cannot read properties of null (reading 'substring') と出る
const str = null;
console.log(str.substring(1));
このエラーを回避するには下記のようにする
const str = null;
if(str !== null && str !== undefined){
console.log(str.substring(1));
}
この記述はよくやるけど、冗長なので2020年から使われるようになったOptional Chaining演算子を利用することで ?.演算子を使って下記のように書き換えが可能
const str = null;
console.log(str?.substring(1));
こうするとコンソールにエラーは出ずに、undefinedと出る
?.が値を判定して、値が空だった場合は無条件でundefinedを返してくれる
?.演算子は、下記のような複数階層に渡るアクセスでより効果を発揮する
obj?.title?.length
Null合体演算子
似たようなnullに関わる演算子 Null合体演算子
let value = null;
console.log(value ?? '既定値');
既定値を伴う式を表現したいときによく使われる。値が入っていないと既定値が返され、値が入っていたら値が入る
モジュール
モジュールは、アプリを機能単位に分割するための仕組み
変数やメソッドの競合リスクが減らせる。
非モジュールの時は、1つのJSファイルが肥大化していたけど
モジュールにすると、1つのファイルは1モジュールでコンパクトで見やすい
モジュールファイルの例 App.jsとする
exportキーワードをつけたものだけが外部からアクセスできる
この場合、APP_TITLE定数は外部からいじれない。
const APP_TITLE = 'Reactアプリ';
export function getTriangle(base, height) {
return base * height / 2;
}
export class Article {
getAppTitle() {
return APP_TITLE;
}
}
モジュールファイルを実行ファイルでimport命令で受け取って使う
App.jsモジュールのgetTriangle関数とArticleクラスをimportで受け取っている
Dart Sassのimportと似ているなぁ
モジュールは現在のjsファイルから相対パスで表す。
EJSに似ているなぁ。
また、モジュール側でexportしていても、import側で明示していない場合は、アクセスできない。
例 実行ファイル module_basic.js
import { Article, getTriangle } from './App.js';
console.log(getTriangle(10, 5));
const a = new Article();
console.log(a.getAppTitle());
html側では実行ファイルを読み込む
その際、type="module"属性にしてモジュール型のコードであることを宣言しないといけない。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React実践入門</title>
</head>
<body>
<script type="module" src="module_basic.js"></script>
</body>
</html>
HTMLはローカルで開くだけだとクロスオリジンエラーが出るので、VSCodeのプラグインのLive Serverを有効にして、右クリック Open with Live Serverで確認ができる
importしたモジュールのメンバーは as句でエイリアスをつけることもできる
変更前
import { getTriangle } from './App.js';
console.log(getTriangle(10, 5));
As句 で関数名をtriと別名にして簡略化できる
import { getTriangle as tri } from './App.js';
console.log(tri(10, 5));
アスタリスクで、モジュール配下のメンバーをまとめてインポートできる
メンバとはクラス、関数、変数、定数などのこと
変更前
import { Article, getTriangle } from './App.js';
アスタリスクを使った場合
import * as app from './App.js';
console.log(app(10, 2));
デフォルトのエクスポート
モジュール1つにつき、1つだけ既定のエクスポートを設定できる
export default class Util {
static getCircleArea(radius) {
return (radius ** 2) * Math.PI;
}
}
ちなみにstaticフィールドは2022年から作られた
既定エクスポートのインポートは下記のように呼び出す
import Util from './Util.js';
console.log(Util.getCircleArea(10));
既定メンバをその他のメンバーとまとめてインポートもできる
import Util { getTriangle } from './Util.js';
これはすべて静的インポートで、初期読み込みのタイミングですべてのモジュールをまとめてインポートする。
初期起動に不要なモジュールは動的インポートで、実行時に条件などに応じてモジュールをインポートできる。
難しい。thenメソッドを使った書き方をすると非同期でインポートできる
import('./App.js').then(app => {
console.log(app.getTriangle(10, 5));
const a = new app.Article();
console.log(a.getAppTitle());
});