フロントエンド学習 ECMAScript 第2回

前回に引き続きECMAScriptをまとめていきたいと思います。

今回は前回紹介できなかった、関数 組み込みオブジェクト オブジェクト指向構文 についてです。


関数

デフォルト値

従来のJavaScriptでは、引数にデフォルト値を設定することができず、引数が渡せされているかのチェックをし、デフォルト値をセットするといった処理を書かなければなりませんでしたが、ECMAScript6では引数にデフォルト値をセットできるようになりました。

1
2
3
4
5
6
7
8
9
const show = (name = '太郎') => {
console.log(`私の名前は${name}です`);
};

show();
// 結果:私の名前は太郎です

show('花子');
// 結果:私の名前は花子です

このデフォルト値が適応されるのは、引数が未指定の場合のみです。
nullやfalseなどが渡された場合は、デフォルト値は無視されます。

また、デフォルト値に関数を指定できるので、引数がない場合にエラーと返すという処理も実現可能です。

1
2
3
4
5
6
7
8
9
10
const required = () => {
console.error('必須項目になります、入力して下さい');
};

const hoge = (value = required()) => {
return value;
};

hoge();
// 結果:必須項目になります、入力して下さい

可変長引数

仮引数に ... を付与することで、可変長引数となります。
これは、渡された任意個数の引数を配列としてまとめて受け取る機能です。

従来のJavaScriptではargumentsオブジェクトを介す必要がありましたが、このようなしくみが用意された事によりぐんとコードの可読性が向上しましたね!

1
2
3
4
5
6
7
8
9
10
const sum = (...args) => {
let result = 0;
for (let arg of args) {
result += arg;
}
return result;
}

console.log(sum(10, 20, 30));
// 結果:60

アロー関数

アロー関数を利用することで、関数リテラルをシンプルに記述できます。

[従来の関数リテラルでの表記]

1
2
3
4
let data = [1, 2, 3];
let formatted = data.map((value, index) => {
return value * value;
});

[アロー関数による表記]

1
2
let data = [1, 2, 3];
let formatted = data.map((value, index) => value * value);

組み込みオブジェクト

Promiseオブジェクト

Promiseを使用することで、非同期処理を同期処理のように扱うことができます。
jQueryの$.defenedやAngularJSの$.qなど、類似の機能が提供されていることもあり利用した方も多いかと思います。

以下は文字列が渡されると2000ミリ秒後に「入力値はXXです」を、文字列が空の場合には「入力値が空です」というメッセージを返す非同期処理の例です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const hoge (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value) {
resolve(`入力値は${value}です`);
}
reject('入力値が空です');
}, 2000);
});
};

hoge('山田太郎').then(
response => {console.log(response)},
error => {console.log(error)}
);

// 結果:入力値は山田太郎です

私は、このPromiseで結構混乱することが多かったです・・・。

もっと理解を深めたいという方はJavaScript Promiseの本も参考にしてみてください。

Proxyオブジェクト

Proxyオブジェクトは、オブジェクトの標準的な操作を独自の操作で差し替えるためのものです。

以下のように、存在しないプロパティを取得しようとした際に、デフォルト値として「?」を返すようにカスタマイズできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
let obj = {hoge: 'ほげ', foo: 'ふー'};

const p_obj = new Proxy(obj, {
get(target, prop) {
return prop in target ? target[prop] : '?';
}
});

console.log(p_obj.hoge);
// 結果:ほげ

console.log(p_obj.noting);
// 結果:?

上記では、getメソッドを使ってターゲット(target)のプロパティ(prop)が存在していれば、その値である「target[prop]」を返し、存在していなければ、「?」を返す例となります。

1
2
3
4
ner Proxy(target, handler);

target:操作を差し込む対象のオブジェクト
handler:ターゲットの操作を定義したオブジェクト

上記のhandlerでは、以下のメソッドが定義できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// プロパティの取得
get function(target, prop, receiver) -> any

// プロパティの設定
set function(target, prop, value, receiver) -> boolean

// for...in命令による列挙
enumerate function(target) -> [string]

// for...of命令による列挙
iterate function(target) -> iterator

// delete命令によるプロパティの削除
deleteProperty function(target, prop) -> boolean

少しわかりづらいかもしれませんが、例えば上記のsetメソッドを使って、Proxyを適応したオブジェクトに数字を入れると値が倍になって格納されるようにしたりもできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let obj = {};

const p_obj = new Proxy(obj, {
set(target, prop, value){
if(typeof value === "number") {
value *= 2;
}
target[prop] = value;
}
});

p_obj.a = 100;
p_obj.b = 200;
p_obj.c = "200";

console.log(p_obj);
// 結果:{a: 200, b:400, c:"200"}

オブジェクトに格納する際に、処理を挟みたい場合など結構ありそうなので使用頻度は高そうですね!

Map / Set

ECMAScript6では、コレクションを管理するための専用オブジェクトとして、Map / Setが追加されました。

まず、Mapオブジェクトは、キー/値のセットでデータを管理するオブジェクトです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
let obj = {};

// マップの生成&値の登録
let m = new Map();
m.set('hoge', 'ほげ');
m.set('foo', 'ふー');
m.set('piyo', 'ぴよ');

// オブジェクトをキーに設定
m.set(obj, 'オブジェクト');

// 値の取得
console.log(m.get('hoge'));
// 結果:ほげ
console.log(m.get(obj));
// 結果:オブジェクト

// オブジェクトリテラルでマップにアクセス
console.log(m.has('hoge'));
// 結果:true

// マップのキーを列挙
for (let key of m.keys()) {
console.log(key);
}
// 結果:hoge, foo, piyo, {}

// マップの値を列挙
for (let value of m.values()) {
console.log(value);
}
// 結果:ほげ, ふー, ぴよ, オブジェクト

// マップのキー/値を列挙
for (let [key, value] of m) {
console.log(`${key}:${value}`);
}
// 結果:hoge:ほげ、foo:ふー、piyo:ぴよ、[Object Object]:オブジェクト

// キーの削除
m.delete('hoge');

// すべてのキーを削除
m.clear();

Mapを利用することで

  • 任意の値でキーを設定できる
  • マップのサイズをsizeプロパティで簡単に取得できる
  • クリーンなマップを作成できる

という利点があります。

次に、Setオブジェクトについてです。
Setオブジェクトを利用することで、重複しない値の集合を管理できます。

重複した値が追加された場合は、無視されます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let obj = {};

// セットの生成&値の登録
let s = new Set();
s.add(1);
s.add(2);
s.add(3);

// 重複した内容は追加されない
s.add(3);

// オブジェクト型を登録
s.add(obj);

// セットの内容を確認
console.log(s.size);
// 結果:3

console.log(s.has(1));
// 結果:true

// セットから値を削除
s.delete(1);

// セットの値を列挙
for (let value of s) {
console.log(value);
}

// セットの内容をクリア
s.clear();

Map / Setでは、Object型をはじめ任意の方を指定できますが、参照型をキーにした場合、getやhasメソッドでの取得では変数objとリテラル{}の参照先は別なので、値をた正しく参照することができないので注意が必要です。

オブジェクト指向構文

オブジェクトリテラルがよりシンプルに表現できるようになりました。

変数を同名プロパティに設定する

プロパティの名前と、その値が同名の場合に値の指定を省略できるようになりました。

1
2
3
4
5
{title: title, price: price, publish: publish}



{title, price, publish}

メソッドを定義する

従来は、 名前:function(params){...} のように関数型のプロパティとして表記しなければなりませんでしたが、ECMAScript6では 名前(params){...} という表記が許されるようになりました。

1
2
3
4
5
6
7
let book = {
title: 'タイトル',
price: 2000,
toString() {
console.log(`${this.title}:${this.price}`);
}
};

プロパティ名を動的に生成できる

プロパティ名を [] で括ることで、式の値から動的にプロパティ名を生成できるようになりました。

1
2
3
4
5
6
7
8
9
let i = 0;
let data = {
['hoge' + ++i]: 15,
['hoge' + ++i]: 20,
['hoge' + ++i]: 25,
};

console.log(data);
// 結果:{'hoge1': 15, 'hoge2': 20, 'hoge3': 25};

class命令

ECMAscript6では、いよいよclass命令が利用できるようになりました。やっとか・・・という感じがします( 笑 )

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
show() {
return `${this.name}は${this.sex}です。`;
}
}

let p = new Person('花子', '女');
console.log(p.show());
// 結果:花子は女です。

とても直感的に書けるようになりました。

ただ、 関数として呼び出すことはできない 定義前の呼び出しはできない という点にご注意ください。

static修飾子やgetter、setterなども利用できます。

[static修飾子の利用例]

1
2
3
4
5
6
7
8
class Figure  {
static triangle(base, height) {
return base * height / 2;
}
}

console.log(Figure,triangle(10, 5));
// 結果:25

[getter、setterの利用例]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
get age() {
return this._age;
}
set age(value) {
this._age = value;
}
show() {
return `${this.name}は${this.sex}、${this.age}歳です。`;
}
}

let p = new Person('花子', '女');
p.age = 10;
console.log(p.show());
// 結果:花子は女、10歳です。

※ECMAscript6では、classブロックの中で let age = 10; のような、 インスタンスフィールドを定義することはできない 為、上記のgetter、setterを利用しましょう。

extendsキーワード

extendsキーワードを利用することで、既存のclassを軽傷してサブクラスを定義することができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
show() {
return `${this.name}は${this.sex}です。`;
}
}

class BusinessPerson extends Person {
constructor(name, sex, clazz) {
super(name, sex);
this.clazz = clazz;
}
show() {
return `${super.show()} 役職は${this.clazz}です。`;
}
}

let bp = new BusinessPerson('花子', '女', '主任');
console.log(bp.show());
// 結果:花子は女です。 役職は主任です。

ECMAScript6では、Array / Date / Errorなどの組み込みオブジェクトをextendsキーワードで継承できるようになりました。

独自の例外オブジェクトを実装したい場合など便利そうですね(゜゜)


いかがでしたでしょうか。
こうしてまとめてみると、まだまだ実務で生かせていない部分などもちらほらあり個人的にもこうして勉強&まとめることでよい勉強になりました!

今回でECMAScript6については終わりとなります。
次回からは、Reduxの学習に入っていきます!

このエントリーをはてなブックマークに追加