📝
Destructuring assignmentのご利用は計画的に
@armorik83 です。ES2015 に勢いがあるので本日 2 本目の記事です(前の記事はESLint 1.0.0 新ルールまとめ)。
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
構文例
es6.js
const [a, b] = [1, 2];
const [c, d, ...rest] = [1, 2, 3, 4, 5];
const {e, f} = {e: 1, f: 2};
es6.js
function f({a, b}) {
return a + b;
}
f({a: 1, b: 2}); // 3
es6.js
class Person {
constructor({first, last}) {
this.first = first;
this.last = last;
}
}
const p = new Person({first: 'John', last: 'Appleseed'});
様々なケースを試す
今回は挙動を検証したいだけの記事なので、この構文の何が便利かという疑問については別の機会とします。
1
function f({ a, b }) {
return a + b;
}
f({ a: 1, b: 2 }); // 3
1-1. a に初期値
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 に初期値
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]自体に初期値(空オブジェクト)
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]自体に初期値(プロパティ値指定)
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
function f({ a, b, c, d }) {
return a + b + c + d;
}
f({ a: 1, b: 2, c: 3, d: 5 }); // 11
2-1. 2 つに分割
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. 初期値を指定
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]自体に初期値を指定
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]自体に初期値を指定
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 初期値と引数初期値を揃える
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 の場合
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 構文の方が便利かもと思い始めている
ご利用は計画的に。
Discussion