配列の分割代入

名前の情報を持つシンプルな配列を分割代入してみます。

let people = [ 'taccaya', 'tanaka', 'suzuki' ];
let [engineer, designer, manager] = people;

console.log(engineer);
console.log(designer);
console.log(manager);
>> taccaya
>> tanaka
>> suzuki

配列の中から一人だけデザイナーとして取り出します。

let people = [ 'taccaya', 'tanaka', 'suzuki' ];
let [, designer] = people;

console.log(designer);
>> tanaka

スプレッド演算子を使って、CEOとメンバーに取り分けてみます。

let people = [ 'taccaya', 'tanaka', 'suzuki', 'yamashita', 'higashino' ];
let [ ceo, ...member ] = people;

console.log(ceo);
console.log(member);
>> "taccaya"
>> ["tanaka", "suzuki", "yamashita", "higashino"]

オブジェクトの分割代入

人物の情報を持つシンプルなオブジェクトを分割代入してみます。

let person = { name: 'taccaya', age: 23, sex: 'male' };
let { name, age, sex } = person;

/** 省略せずに書いたらこうなる
   * let {cute: cute, cool: cool, passion: passion} = individuals;
   *
   * 順不同
   * let {passion, cute, cool} = individuals;
   */
console.log(name);
console.log(age);
console.log(sex);
>> taccaya
>> 23
>> male

デフォルトの値を指定して、参照可能なプロパティがなかった場合にその値を代入されるようにします。

let person = { name: 'taccaya', age: 23, sex: 'male' };
let { name, age, sex, weight = 62 } = person;

console.log(weight);
>> 62

変数名をpersonオブジェクトが持つプロパティ名と別にしたい場合は { プロパティ名: 変数名 } のように指定して分割代入を行います。

let person = { name: 'taccaya', age: 23, sex: 'male' };
let { name: userName, age: userAge, sex: userSex } = person;

console.log(userName);
console.log(userAge);
console.log(userSex);
>> taccaya
>> 23
>> male

メソッドもプロパティなので、分割代入が可能です。

let methods = {
    hello () {
    console.log('hello, Destructuring assignment!');
    }
}

let { hello } = methods;

hello()
>> hello, Destructuring assignment!

複雑な階層の分割代入

階層が深くなった配列で分割代入を行います。

Array in Array 配列の中に配列がある場合

let people = [
    [ 'taccaya', 23, 'male' ],
    [ 'tanaka', 26, 'female' ],
    [ 'suzuki', 29, 'male' ]
]

//二番目の値を取得
let [, [name, age, sex ]] = people;

console.log(name)
console.log(age)
console.log(sex)
>> tanaka
>> 26
>> female

Object in Array 配列の中にオブジェクトがある場合

let people = [
    { name: 'taccaya',age: 23, sex: 'male' },
    { name: 'tanaka', age: 26, sex: 'female' },
    { name: 'suzuki', age: 29, sex: 'male' }
]

//最初の値を取得
let [{name, age, sex }] = people;

console.log(name)
console.log(age)
console.log(sex)
>> taccaya
>> 23
>> male

Array in Array && Object オブジェクトの中にオブジェクトや配列がある場合

let team = {
    managers: {chief: 'suzuki', others: [ 'yamashita', 'nishida' ]},
    designers: {chief: 'tanaka', others: [ 'yomoda', 'ota' ]},
    engineers: {chief: 'taccaya', others: [ 'toyota', 'aihara' ]}
}

//マネージメント陣を取り出す
let {managers: { chief: chiefName, others: othersName }} = team;
console.log(chiefName)
console.log(othersName)
>> suzuki
>> ["yamashita", "nishida"]

もっと複雑な階層での分割代入

もっと複雑な階層構造をもつオブジェクトを分割代入していきましょう。

ちょうど先日、現在ぼくが開発中のChatFormというサービスの決済システムを実装した際、Stripeのテスト用JSONオブジェクトを取り扱うことがありました。

let stripeJson = {
  "created": 1326853478,
  "livemode": false,
  "id": "plan.created_00000000000000",
  "type": "plan.created",
  "object": "event",
  "request": null,
  "pending_webhooks": 1,
  "api_version": "2018-11-08",
  "data": {
    "object": {
      "id": "plan_00000000000000",
      "object": "plan",
      "active": true,
      "aggregate_usage": null,
      "amount": 1000,
      "billing_scheme": "per_unit",
      "created": 1547627955,
      "currency": "jpy",
      "interval": "month",
      "interval_count": 1,
      "livemode": false,
      "metadata": {
      },
      "nickname": "ビジネスプラン",
      "product": "prod_00000000000000",
      "tiers": null,
      "tiers_mode": null,
      "transform_usage": null,
      "trial_period_days": null,
      "usage_type": "licensed"
    }
  }
}

こちらのテスト用のJSONオブジェクトを使って、まずは分割代入を行わずに変数に値を代入してみます。

let stripeObj = JSON.parse(stripeJson);

let id = stripeObj.id;
let type = stripeObj.type;
let plan_id = stripeObj.data.object.id;
let plan_name = stripeObj.data.object.nickname;

console.log(id);
console.log(type);
console.log(plan_id);
console.log(plan_name);
>> plan.created_00000000000000
>> plan.created
>> plan_00000000000000
>> ビジネスプラン

次に、分割代入を使って変数に値を代入していきます。

let stripeObj = JSON.parse(stripeJson);

//階層構造に従って指定
let {
    id,
    type,
    data: {
        object: {
            id: plan_id,
            nickname: plan_name
        }
    }
} = stripeObj;

console.log(id);
console.log(type);
console.log(plan_id);
console.log(plan_name);
>> plan.created_00000000000000
>> plan.created
>> plan_00000000000000
>> ビジネスプラン

ただし、この書き方だとプロパティ名と変数名との違いが、ぱっと見でわかりづらいですね。

一応プロパティ名はクオーテーションで囲むように統一すれば、両者の区別がつきやすくなったりします。

let stripeObj = JSON.parse(stripeJson);

//階層構造に従って指定
let {
    id,
    type,
    'data': {
        'object': {
            'id': plan_id,
            'nickname': plan_name
        }
    }
} = stripeObj;

応用・実践的な例

Vue.jsやNuxt.jsなど、フロントエンドのフレームワークを使っていると、関数・メソッドの引数として分割代入を使う機会がよくあります。

個人的に、最初に分割代入を見たときは、とっつきにくく感じましたが、慣れれば快適にコーディングできるようになります。

例えば、Vuexのストアでアクションを定義する際、コンテキスト(state, getters, dispatch, commitなどのプロパティを持つ)というオブジェクトが発行され、それを第一引数で受け取ることができます。

コンテキストオブジェクトを使ってコミットを行う場合、次のようになります。

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

...省略

//分割代入を使わない書き方
actions: {
    incrementAction (context) {
        context.commit('increment');
    }
}

分割代入で書くとこのように書けます。

...省略

//分割代入を使う書き方
actions: {
    incrementAction ({commit}) {
        commit('increment');
    }
}

contextから呼び出すひと手間が減る分、コードが短くかけます。

for ofループでも使えます。

let people = [
    [ 'taccaya', 23, 'male' ],
    [ 'tanaka', 26, 'female' ],
    [ 'suzuki', 29, 'male' ]
]

for (const [name, age, sex] of people) {
    console.log(`${name}, ${age}, ${sex}`);
}
>> taccaya, 23, male
>> tanaka, 26, female
>> suzuki, 29, male

関数・メソッドの引数に分割代入を使ってコードの可読性を上げます。

配列にタスクの追加を行うシンプルな関数を、分割代入を使わずに書くとこのようになります。

let tasks = [];

const addTask = (name, label, dayLimit) => {
    tasks.push({
        name: name,
        label: label,
        dayLimit: dayLimit
    });
    return tasks;
}

>> addTask('牛乳を買う', '買い物', 1);
>> [{name: "牛乳を買う", label: "買い物", dayLimit: 1}]

少し冗長に感じますし、呼び出し側だけを見るとdayLimitの引数の意味がわかりません。

そこで分割代入を使って、この問題を解決します。

let tasks = [];

//デフォルト引数{}を定義することで、TypeErrorを起こさないようにしています。
const addTask = ({name, label, dayLimit} = {}) => {
    tasks.push({ name, label, dayLimit });
    console.log(tasks);
}

>> addTask({
        name:'牛乳を買う',
        label: '買い物',
        dayLimit: 1
        });
>> [{name: "牛乳を買う", label: "買い物", dayLimit: 1}]

これだけでかなりいい感じのコードになった気がしますね。

requiredという関数をデフォルト引数として定義することで、「undefinedが渡された場合はエラーを発生させる」といったことも可能です。

let tasks = [];

const required = (param) => {
    throw new Error(`${param}は引数として必須のパラメータです`);
};

//required関数をデフォルト引数に設定することで、name引数が渡されなかった場合のエラーハンドリングが可能
const addTask = ({name = required('name'), label, dayLimit} = {}) => {
    tasks.push({
        name,
        label,
        dayLimit,
    });
    console.log(tasks);
}

>> addTask();
>> Uncaught Error: nameは引数として必須のパラメータです

まとめ

分割代入はコードの記述量が少なくなるのはもちろん、可読性、エラーハンドリングなど、多様な使い方があるなぁと感じます。

ここに書いた例以外にも、もっと便利にコーディングができるようなノウハウを見つけていきたいですね。

PROFILE

はじめまして、大阪のWeb制作会社Yuapです。「Yuap(ユアプ)」とは英語で「あなたのプランナー」という、Your Plannerの頭文字をとった社名です。 ホームページ制作|Webシステム開発|Webコンサルティングなどの業務を通じて、みなさまの事業の成長に貢献できるようなプランナーになれるよう、日々チャレンジしております。

※お問い合わせ・お見積りは無料です。 IT|ホームページ|Webシステムのことならなんでもお気軽にご相談ください。お問い合わせはこちらから。