KDE BLOG

バイブス

【ES2015】分割代入の基本と便利な使い方

分割代入 (Destructuring assignment)とは

ES2015で導入されていた機能でざっくり言うと、複数の値を、一度に複数の変数に代入するためのもの

配列での使い方

配列の各要素を取得する基本型。

const ary = [1, 2, 3];
const [a, b, c] = ary;

console.log(a); // -> 1
console.log(b); // -> 2
console.log(c); // -> 3

特定の要素だけ取得する場合は、それ以外の要素に対応する箇所は空欄のままカンマで区切る。

const ary = [1, 2, 3];
const [, , a] = ary;

console.log(a); // -> 3

空欄のままカンマで区切った要素は undefined が入る。

const [a, b, c] = [1, , 3];
console.log(b); // -> undefined

スプレッド演算子を使って複数の要素も取得可能。
下記の場合は指定されていない残りの値が変数 cに配列として代入される。

const ary = [1, 2, 3, 4, 5];
const [a, , b, ...c] = ary;

console.log(a); // -> 1
console.log(b); // -> 3
console.log(c); // -> [4, 5]

ES2015で導入されたデフォルト引数の記述形式で、規定値の指定ができる。

const [a = 1, b = 2, c = 3] = [, , 5];
console.log(a); // -> 1
console.log(b); // -> 2
console.log(c); // -> 5

オブジェクトでの使い方

オブジェクトから一度に複数のkeyの値を取り出すことができる。

const bob = {
  age: 30,
  gender: 'male',
  country: 'USA'
};

const {age, gender} = bob;
console.log(age); // -> 30
console.log(gender); // -> male

keyと同名の変数でないといけないかというとそんなことはなく、:(コロン)で繋いで変数名を定義すると、その変数名に変更できる。

const {age: nenrei , gender: seibetsu} = bob;
console.log(nenrei); // -> 30
console.log(seibetsu); // -> male

存在しないkeyを代入しようとすると、配列の時と同様に undefinedが入る。

const {weight: taiju} = bob;
console.log(taiju); // -> undefined

配列と同様にデフォルト引数の形式で規定値の指定が可能。

const {
  age: nenrei = 20,
  gender: seibetsu,
  weight: taiju = '70kg'
} = bob;

console.log(nenrei);   // -> 30
console.log(seibetsu); // -> male
console.log(taiju);    // -> 70kg

便利な使い方

値の入れ替え

変数同士の値の入れ替えが簡単に行える。

let [a, b] = [1, 2];
[a, b] = [b, a];
console.log(a); // -> 2
console.log(b); // -> 1

複数の返り値の取得

配列もしくはオブジェクト形式の返り値を一気に取得できる。
もちろんいらないものは除外可能。

function power(num) {
  return [num, num**2, num**3];
}

const [a, b, c] = power(3);
console.log(a); // -> 3
console.log(b); // -> 9
console.log(c); // -> 27

関数の引数を分かりやすく表現

基本的に、引数が多い関数はちゃんと考慮しないと引数にどんな値を入れたらいいかわかりにくくなるが、分割代入を応用すると分かりやすく明示することができる。

例えば下記は、move関数の呼び出しと宣言をしているが、どんな引数をどんな順番で入れればよいか分かりにくい。

// 呼び出し
move('.target', 10, 30, 300, 'ease-in');

// 宣言
function move(targetSelector, x, y, duration, easing) {
  // 何らかの処理…
}

オブジェクトを引数に使えば、関数の呼び出し側では分かりやすくなるが、関数の宣言個所では仮引数が一つとなり、どんなプロパティのオブジェクトを渡せばよいか、すぐには分かりにくい。

// 呼び出し
move({
  targetSelector:'.hoge',
  x: 10,
  y: 30,
  duration: 300,
  easing: 'ease-in'
});

// 宣言
function move(params) {
  const targetSelector = params.targetSelector;
  const x = params.x;
  const y = params.y;
  const duration = params.duration;
  const easing = params.easing;
}

分割代入を使って宣言を書くと下記になる。
引数に渡すべきオブジェクトのプロパティの名前と値のデータ型の把握がしやすい。
※引数が空でも問題がないように、デフォルト引数として {} を代入。

// 宣言
function move({targetSelector = '.target', x = 0, y = 0, duration = 0, easing = 'linear'} = {}) {
  // 何らかの処理…
}

ループ処理

foreachなどのループ処理のときに使用すると、ダイレクトにプロパティ名で参照できてコードの見通しがよくなる。

const people = [
  {name: 'Bob', age: 30, country: 'USA'},
  {name: 'Anne', age: 25, country: 'Germany'},
  {name: 'Taro', age: 22, country: 'Japan'}
];

// 通常時(分割代入を使わない)
people.forEach(person => {
  console.log(`名前:${person.name} 年齢:${person.age} 国籍:${person.country}`);
});

// 分割代入を使う
people.forEach(({name, age, country}) => {
  console.log(`名前:${name} 年齢:${age} 国籍:${country}`);
});

JSONや、ネストされたオブジェクトや配列から値を取得

普通に取ろうとすると面倒なデータからも、分割代入を使うと階層をたどって取得できる。

const people = [
  {
    name: 'Bob',
    age: 30,
    country: 'USA',
    family: {
      child: [
        {
          name: 'Jack',
          age: 8,
          hobby: ['video game', 'football']
        },
        {
          name: 'Tom',
          age: 1,
          hobby: ['walk', 'eat', 'sleep']
        }
      ]
    }
  },
  {
    name: 'Anne',
    age: 25,
    country: 'Germany',
    family: {
      child: [
        {
          name: 'Clara',
          age: 3,
          hobby: ['play house', 'sing']
        }
      ]
    }
  }
];

const [bob, anne] = people;

const {
  name: bobName,
  age: bobAge,
  country: bobCountry,
  family: bobFamily,
  family: {
    child: [...bobChild],
    child: [bobChild1, bobChild2],
    child: [{
      name: bobChild1Name,
      age: bobChild1Age,
      hobby: [...bobChild1Hobby]
    }, {
      name: bobChild2Name,
      age: bobChild2Age,
      hobby: [bobChild2Hobby1, bobChild2Hobby2]
    }]
  }
} = bob;

console.log(bob);       // -> {name: "Bob", age: 30, country: "USA", family: {…}}
console.log(bobName);   // -> Bob
console.log(bobFamily); // -> {child: Array(2)}
console.log(bobChild);  // -> [{name: "Jack", age: 8, hobby: Array(2)}, [{name: "Tom", age: 1, hobby: Array(3)}]

console.log(bobChild1);      // -> {name: "Jack", age: 8, hobby: Array(2)}
console.log(bobChild1Name);  // -> Jack
console.log(bobChild1Age);   // -> 8
console.log(bobChild1Hobby); // -> ["video game", "football"]

console.log(bobChild2Name);   // -> Tom
console.log(bobChild2Age);    // -> 1
console.log(bobChild2Hobby1); // -> walk
console.log(bobChild2Hobby2); // -> eat

const {
  family: {
    child: [{
      hobby: [, anneChildHobby2]
    }]
  }
} = anne;

console.log(anneChildHobby2); // -> sing

JSONの場合も同じ要領でできる。

const jsonData = [
  {
    "name": "Bob",
    "age": 30,
    "country": "USA",
    "family": {
      "child": [
        {
          "name": "Jack",
          "age": 8,
          "hobby": ["video game", "football"]
        },
        {
          "name": "Tom",
          "age": 1,
          "hobby": ["walk", "eat", "sleep"]
        }
      ]
    }
  },
  {
    "name": "Anne",
    "age": 25,
    "country": "Germany",
    "family": {
      "child": [
        {
          "name": "Clara",
          "age": 3,
          "hobby": ["play house", "sing"]
        }
      ]
    }
  }
];

const [bob, anne] = jsonData;

const {
  "name": bobName,
  "age": bobAge,
  "country": bobCountry,
  "family": bobFamily,
  "family": {
    "child": [...bobChild],
    "child": [bobChild1, bobChild2],
    "child": [{
      "name": bobChild1Name,
      "age": bobChild1Age,
      "hobby": [...bobChild1Hobby]
    }, {
      "name": bobChild2Name,
      "age": bobChild2Age,
      "hobby": [bobChild2Hobby1, bobChild2Hobby2]
    }]
  }
} = bob;

console.log(bob);       // -> {name: "Bob", age: 30, country: "USA", family: {…}}
console.log(bobName);   // -> Bob
console.log(bobFamily); // -> {child: Array(2)}
console.log(bobChild);  // -> [{name: "Jack", age: 8, hobby: Array(2)}, [{name: "Tom", age: 1, hobby: Array(3)}]

console.log(bobChild1);      // -> {name: "Jack", age: 8, hobby: Array(2)}
console.log(bobChild1Name);  // -> Jack
console.log(bobChild1Age);   // -> 8
console.log(bobChild1Hobby); // -> ["video game", "football"]

console.log(bobChild2Name);   // -> Tom
console.log(bobChild2Age);    // -> 1
console.log(bobChild2Hobby1); // -> walk
console.log(bobChild2Hobby2); // -> eat

const {
  "family": {
    "child": [{
      "hobby": [, anneChildHobby2]
    }]
  }
} = anne;

console.log(anneChildHobby2); // -> sing

まとめ

慣れないととっつきにくく感じるかもしれないが、従来の書き方よりも短い記述で済むうえに、コードの見通しもよくなるケースがあるので積極的に取り入れていきたい。

ただ、むやみに使うと逆に可読性が落ちる場合があるので注意したい。

参考