Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save okunokentaro/e6aae7eaa024f296bf55e043fc9cd62d to your computer and use it in GitHub Desktop.
Save okunokentaro/e6aae7eaa024f296bf55e043fc9cd62d to your computer and use it in GitHub Desktop.
Destructuring assignmentのご利用は計画的に
2015/08/03 にQiitaに投稿した記事のアーカイブです
---
@armorik83です。ES2015に勢いがあるので本日2本目の記事です(前の記事は[ESLint 1.0.0新ルールまとめ](http://qiita.com/armorik83/items/228b97bee25aa33c9850))。
# ES2015の活用
今、業務ではBabelの許可が出ているので、フルにES2015(ES6とも呼ばれていました)を活用しています。Class構文や`Import`はTypeScript経験も手伝ってもはや当たり前になっていますが、最近使い始めたDestructuring assignmentは迂闊に使うと(特にチーム内での)混乱に繋がるぞ…と危険視し始めたので、挙動を確認しておきます。
# Destructuring assignment
> https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
ブラウザのサポートはまだまだで、現状Babelの変換結果に頼っている状態です。なお個人的な感想ですが、Babel開発者はPRなどで送られた仕様外の挙動は容赦なくrejectするため、準拠度合いには一定の信頼を持っています。
以下の挙動確認はすべてBabel 5.8.xによるものです。[Babel Try it out](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=)
## 構文例
```js:es6.js
const [a, b] = [1, 2];
const [c, d, ...rest] = [1, 2, 3, 4, 5];
const {e, f} = {e: 1, f: 2};
```
```js:es6.js
function f({a, b}) {
return a + b;
}
f({a: 1, b: 2}); // 3
```
```js:es6.js
class Person {
constructor({first, last}) {
this.first = first;
this.last = last;
}
}
const p = new Person({first: 'John', last: 'Appleseed'});
```
# 様々なケースを試す
今回は挙動を検証したいだけの記事なので、この構文の何が便利かという疑問については別の機会とします。
## 1
```js
function f({a, b}) {
return a + b;
}
f({a: 1, b: 2}); // 3
```
### 1-1. aに初期値
```js
function f({a = 3, b}) {
return a + b;
}
f({a: 1, b: 2}); // 3
f({b: 2}); // 5
f({}); // null
f(); // Cannot read property 'a' of undefined
```
### 1-2. a, bに初期値
```js
function f({a = 3, b = 5}) {
return a + b;
}
f({a: 1, b: 2}); // 3
f({a: 1}); // 6
f({b: 2}); // 5
f({}); // 8
f(); // Cannot read property 'a' of undefined
```
### 1-3. {a, b}に初期値、argument[0]自体に初期値(空オブジェクト)
```js
function f({a = 3, b = 5} = {}) {
return a + b;
}
f({a: 1, b: 2}); // 3
f({a: 1}); // 6
f({b: 2}); // 5
f({}); // 8
f(); // 8
```
### 1-4. {a, b}に初期値、argument[0]自体に初期値(プロパティ値指定)
```js
function f({a = 3, b = 5} = {a: 7, b: 11}) {
return a + b;
}
f({a: 1, b: 2}); // 3
f({a: 1}); // 6
f({b: 2}); // 5
f({}); // 8
f(); // 18
```
この辺りから、Destructuring assignmentを学習していない者には厳しい構文になってきます。
## 2
```js
function f({a, b, c, d}) {
return a + b + c + d;
}
f({a: 1, b: 2, c: 3, d: 5}); // 11
```
### 2-1. 2つに分割
```js
function f({a, b}, {c, d}) {
return a + b + c + d;
}
f({a: 1, b: 2}, {c: 3, d: 5}); // 11
f({c: 3, d: 5}, {a: 1, b: 2}); // null
```
### 2-2. 初期値を指定
```js
function f({a = 7, b = 11}, {c = 13, d = 17}) {
return a + b + c + d;
}
f({a: 1, b: 2}, {c: 3, d: 5}); // 1 + 2 + 3 + 5 = 11
f({a: 1}, {c: 3}); // 1 + 11 + 3 + 17 = 32
f({}, {}); // 7 + 11 + 13 + 17 = 48
f({}); // Cannot read property 'c' of undefined
f(); // Cannot read property 'a' of undefined
```
### 2-3. argument[0]自体に初期値を指定
```js
function f({a = 7, b = 11} = {a: 19, b: 23, c: 29, d: 31}, {c = 13, d = 17}) {
return a + b + c + d;
}
f({a: 1, b: 2}, {c: 3, d: 5}); // 1 + 2 + 3 + 5 = 11
f({a: 1}, {c: 3}); // 1 + 11 + 3 + 17 = 32
f({}, {}); // 7 + 11 + 13 + 17 = 48
f(void 0, {}); // 19 + 23 + 13 + 17 = 72
f({}); // Cannot read property 'c' of undefined
f(); // Cannot read property 'c' of undefined
```
### 2-4. argument[0], argument[1]自体に初期値を指定
```js
function f({a = 7, b = 11} = {a: 19, b: 23, c: 29, d: 31}, {c = 13, d = 17} = {a: 37, b: 41, c: 43, d: 47}) {
return a + b + c + d;
}
f({a: 1, b: 2}, {c: 3, d: 5}); // 1 + 2 + 3 + 5 = 11
f({a: 1}, {c: 3}); // 1 + 11 + 3 + 17 = 32
f({}, {}); // 7 + 11 + 13 + 17 = 48
f(void 0, {}); // 19 + 23 + 13 + 17 = 72
f({}); // 7 + 11 + 43 + 47 = 108
f(); // 19 + 23 + 43 + 47 = 132
```
# 3
## 3-1. Object初期値と引数初期値を揃える
```js
function f({a = 1, b = 2} = {a: 1, b: 2}) {
return a + b;
}
f({a: 3, b: 5}); // 8
f({a: 3}); // 5
f({b: 5}); // 6
f({a: null}); // 2
f({b: null}); // 1
f({a: void 0}); // 3
f({b: void 0}); // 3
f({}); // 3
f(); // 3
function g(a = 1, b = 2) {
return a + b;
}
g(3, 5); // 8
g(3); // 5
g(5); // 7
g(void 0, 5); // 6
g(null, 5); // 5
g(); // 3
```
## 3-2. TypeScriptの場合
```ts:typescript.ts
interface I {
a?: number;
b?: number;
}
function f({a = 1, b = 2}: I = {a: 1, b: 2}) {
return a + b;
}
f({a: 3, b: 5}); // 8
f({a: 3}); // 5
f({b: 5}); // 6
f({a: null}); // 2
f({b: null}); // 1
f({a: void 0}); // 3
f({b: void 0}); // 3
f({}); // 3
f(); // 3
```
TypeScriptでも`interface`を指定すれば書ける。
# 何がしたかったか
- 過剰な例を試して初期値の渡り方を確認したかった
- Object内の初期値と引数自体の初期値を2種類宣言できてしまうところが毒にも薬にも成りうる
- 引数自体の初期値に入れたプロパティは、Object内のプロパティ以外採用されない
- DIコンテナ風の機構を作れないかと考えている
- この用途だと、[Decorators構文](http://qiita.com/armorik83/items/e3a0ce67f569ddc4b432)の方が便利かもと思い始めている
ご利用は計画的に。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment