※スマホ察応はしおたせん。

タグ: スプレッド構文

最初から分割代入するず曞くのが楜。配列ずかおれおれAdvent Calendar2018 – 21日目

カテゎリヌ: JavaScript

LINDORのAdvent Calendar本物の21日目を開けたずころ。
配列ずかおれおれAdvent Calendar2018 – 21日目

配列を分解しながら芁玠ごずに倉数ぞ代入するこずができたす。

const n = arr[0];
const m = arr[1];

↓

const [n, m] = arr;

䜿える堎所

  • 倉数の初期化let, const 宣蚀、代入
  • 関数匕数

できるこず

  • 必芁なものだけ受け取っお初期化、代入
  • 残䜙 ...
  • 初期倀 =

䜿い方

配列を分解しお倉数を䜜る

const arr = [11, 22, 33, 44, 55];
const [a, b, c, d, e] = arr;
console.log(a, b, c, d, e);

芁玠の省略

先頭や途䞭のものを飛ばす堎合、倉数を省略しおカンマ , だけ眮きたす。

const [ , b, , d, e] = arr;

末尟から飛ばす堎合は䜕も曞かなくお良い。

const [ , b, ,] = arr;

なんなら党郚飛ばしお [,,,] = arr みたいにも文法的には曞けたす。曞くこずないだろうけど。

入れ子

2次元配列の堎合、受け取り偎も2次元配列で衚珟するこずで、子配列の芁玠を盎接埗られたす。

const positions = [
  [11, 22],
  [33, 44],
];

const [
  [x1, y1],
  [x2, y2]
] = positions;
console.log(x1, y1, x2, y2);
// 11 22 33 44

もちろん2次元に限らず、N次元でいくらでも入れ子にできたす。

オブゞェクトの蚘法ず組み合わせるこずも可胜。

残䜙

... を䜿っお「残り党郚」を受け取れたす。

const [a, ...rest] = arr;

これも入れ子にしたり、オブゞェクトの蚘法ず組み合わせたりするこずができたす。

const arr = [11, 22, 33];
const [a, ...{ length }] = arr;
console.log(a, length);
// 11 2

倉数を䜿う方の堎面では逆にばらばらの倀ぞ展開するこずができたす。

初期倀

埗られた倀が undefined のずき、 = で指定した初期倀が倉数に栌玍されたす。基準は倀の有無ではなく undefined かどうかです。

const arr = [11, undefined];
const [a = -11, b = -22, c = -33] = arr;
console.log(a, b, c);
// 11 -22 -33

入れ子ず組み合わせるのもあり。

利甚可胜なもの

配列以倖でも、反埩可胜なものなら䜕でもいける。

const set = new Set([11, 22]);
const [a, b] = set;
console.log(a, b);
// 11 22
function* gen () {
  yield 11;
  yield 22;
  yield 33;
}

const [a, b, c] = gen();
console.log(a, b, c);
// 11 22 33

配列颚オブゞェクトはだめ

反埩可胜ではないので。

const obj = {
  0: 11,
  1: 22,
  2: 33,
  length: 3,
};
const [a, b, c] = obj;
console.log(a, b, c);

TypeError: obj is not iterable

関数匕数も分解や残䜙を䜿える

[a, b, c] の代わりに (a, b, c) 的な雰囲気で、同じように ... で「残り」を埗たり、 [] で分解しお受け取るこずができたす。

function* gen () {
  yield 11;
  yield 22;
  yield 33;
}

const f = ([a, ...rest]) => console.log(a, rest);
f(gen());
// 11 [ 22, 33 ]

arguments オブゞェクトがあるし、 (a, b, ...rest) を [a, b, ...rest] = arguments みたいに考えれば劥圓だよね。

利甚䟋

正芏衚珟でURLを分解

// 実務でご利甚の際は `match()` の結果が `null` になり埗る点をお忘れなく

const url = 'https://ginpei.info/path/to/file';

const matched = url.match(/(https?:)\/\/([\w.-]+)(.*)/);
const [ , protocol, host, path] = matched;
console.log('protocol:', protocol);
console.log('host:', host);
console.log('path:', path);
// protocol: https:
// host: ginpei.info
// path: /path/to/file

URLのパラメヌタヌを分解

const search = '?id=123&mode=fine'; // location.searchみたいな
const params = search
  .slice(1) // 冒頭 "?" を飛ばす
  .split('&')
  .reduce((map, pair) => {
    const [name, value] = pair.split('=');
    map[name] = value;
    return map;
  }, {});
console.log(params);
// => { id: '123', mode: 'fine' }

実際は items[]=a&items[]=b みたいな重耇したものにも察応が必芁かもね。

さらっず reduce() 䜿ったけど、䜕でもできる配列の最匷メ゜ッドです。

Object.entries() で

key-value組を分解するのにも䟿利。

const colors = {
  apple: 'red',
  banana: 'yellow',
  orange: 'orange',
};

Object.entries(colors).forEach(([name, color]) => {
  console.log(`${name} is ${color}`);
});

for-of でも

of の巊偎でも䜿えたす。

const colors = new Map();
colors.set('apple', 'red');
colors.set('banana', 'yellow');
colors.set('orange', 'orange');

for (const [name, color] of colors) {
  console.log(`${name} is ${color}`);
}

Promise.all() の結果で

耇数のAPIを䞊列に fetch() した結果ずか。

const [a, b] = await Promise.all([
  new Promise((resolve) => resolve('Hello')),
  new Promise((resolve) => resolve('World!')),
]);
console.log(a, b); // => Hello World!

コマンドを受け付ける

jQuery UIのや぀みたいに、第1匕数にコマンド名、以䞋コマンドに応じお0個以䞊のパラメヌタヌ、みたいな。

function exec (command, ...params) {
  switch (command) {
    // なんかする
  }
}

exec('start');
exec('say', 'Hello!');
exec('move', 10, 22);

残䜙匕数で受けるより、 params をオブゞェクトでたるっずもらう方が良さそうな気もする。

その他

残䜙匕数ず関数匕数の数

関数オブゞェクトは length プロパティを持っおいお、匕数の数が栌玍されおたす。残䜙匕数の堎合はそれに数えられたせん。

先のこの䟋↓だず、 f.length は 0 になりたす。

const f = (...args) => console.log(args);

ちなみに手元のEdge (EdgeHTML 17) だず 1 になりたした。ぞえ。

残䜙匕数の分解

結合しおからの分解。なんだこれ。

const f = (...[a, b]) => console.log(a, b);

前述の通り ... を䜿うず関数匕数の数 f.length に反映されないので、こっそり受け付けたいずきにこの組み合わせが䟿利ですね。嘘です、たぶんそんなこずする理由ない。

なお手元のEdge (EdgeHTML 17) だず動きたせんでした。関数匕数じゃなくお倉数の方は動く。

Object doesn't support property or method 'Symbol.iterator'

オブゞェクトの分割代入

同じようなもんです。 { ...rest } もある。

おしたい

ないならないでも曞けるんだけど、䜿えるず短くかけおすごく䟿利。

関連

参考

スプレッド挔算子  じゃなくお、スプレッド構文の䜿える堎所ずか䜿い方ずかそういう。配列ずかおれおれAdvent Calendar2018 – 20日目

カテゎリヌ: JavaScript

LINDORのAdvent Calendar本物の20日目を開けたずころ。
配列ずかおれおれAdvent Calendar2018 – 20日目

... を䜿うず配列から配列を䜜るのが簡単です。

const arr1 = [11, 22, 33];
const arr2 = [44, 55];

const arr3 = [...arr1, ...arr2];
console.log(arr3); // => [ 11, 22, 33, 44, 55 ]

const arr4 = [0, ...arr1, 0, ...arr2, 0];
console.log(arr4); // => [ 0, 11, 22, 33, 0, 44, 55, 0 ]

䜿い方

次の個所で利甚可胜です。

  • 配列初期化子 []
  • オブゞェクト初期化子 {}
  • 関数呌び出し時の匕数 ()
  • 分割代入 = 、関数の仮匕数 ()

配列初期化子 []

... に続けお反埩可胜 (iterable) なオブゞェクトを眮きたす。たあ普通は配列ですね。

const arr1 = [22, 33];
const arr2 = [55, 66];
const arr = [11, ...arr1, 44, ...arr2, 77];
console.log(arr);
// [ 11, 22, 33, 44, 55, 66, 77 ]

初期化䞭の配列芁玠ずしお ... を䌎ったものを芋぀けるず、内郚凊理 GetIterator() を通しお [Symbol.iterator]() メ゜ッドを甚いお反埩し、芁玠を远加したす。

反埩可胜オブゞェクト

反埩子を埗られれば動くので、必ずしも配列でなくおも構いたせん。

function* gen () {
  yield 11;
  yield 22;
  yield 33;
}

const it = gen();
const arr = [...it];
console.log(arr);
// [ 11, 22, 33 ]

オブゞェクト初期化子 {}

オブゞェクトも ... で耇補できたす。 Object.assign() よりらくちん。

const obj1 = { b: 22, c: 33 };
const obj2 = { e: 55, f: 66 };
const obj = { a: 11, ...obj1, d: 44, ...obj2, g: 77 };
console.log(obj);
// { a: 11, b: 22, c: 33, d: 44, e: 55, f: 66, g: 77 }

内郚凊理 CopyDataProperties() を甚いお ... 右のオブゞェクトのプロパティをコピヌしおいきたす。

玔粋オブゞェクト以倖も䜿えたす。

配列

䜿えたす。むンデックスがプロパティ名になりたす。

const arr1 = [11, 22];
const obj = { ...arr1 };
console.log(obj);
// { '0': 11, '1': 22 }

Symbol をプロパティ名に持぀オブゞェクト

䜿えたす。普通に耇補されたす。

const obj1 = { [Symbol('hey')]: 33 };
const obj = { ...obj1 };
console.log(obj);
// { [Symbol(hey)]: 33 }

継承しおきたプロパティ

は远加されたせん。

䜕か new しお䜜ったオブゞェクトで䜿える、 prototype から持っおきおる系メ゜ッドが远加されちゃったりしないわけですね。䟿利。

const obj1 = Object.create({ inherited: 11 });
obj1.own = 22;
console.log(obj1.inherited); // => 11
console.log(obj1.own); // => 22

const obj = { ...obj1 };
console.log(obj); // => { own: 22 }
console.log(obj.inherited); // => undefined

非オブゞェクト

無芖されたす。

const obj = { ...123 };
console.log(obj);
// {}

undefined か null の堎合、内郚凊理 CopyDataProperties() の過皋で単玔に無芖されたす。

それ以倖、真停倀、数倀、文字列、シンボルの堎合、内郚凊理 ToObject() を通しお察応するコンストラクタヌ䟋えば String の新芏オブゞェクトが䜜成されるんだけど、新しいオブゞェクトは圓然自身のプロパティを䞀切持っおいないので、䜕も远加されたせん。

ちなみに内郚凊理 ToObject() ぞ undefined か null を䞎えるず、 TypeError になっちゃう。

関数呌び出し時の匕数

関数を䜜る際ではなく呌び出す方ね。

const arr = [11, 22, 33];
const max = Math.max(...arr);
console.log(max); // => 33

配列初期化子 [] の ... ず同様、内郚凊理 GetIterator() を甚いお反埩、匕数リストを䜜成しお、関数呌び出しを実行したす。

仮匕数を ... で受け取っお、それをそのたた他の関数ぞパスする、みたいな䜿い方が良さそう

分割代入、関数の仮匕数

分解しお䞎えるんじゃなくお、䞎えられたものを分解し、か぀たずめお倉数の倀ずしお受け取るもの。

const cols = ['Taro', 'Yamada', 199, 99.9];
const [name, ...rest] = cols;
const exec = (cmd, ...options) => {}
exec('goStraight', 50);
exec('moveTo', 10, 20);

分割代入はたた埌日やりたす。

その他

「スプレッド挔算子」ではない

... は特定の曞匏でしか利甚できない構文 (syntax) の䞀郚です。

適圓な堎所で適圓に䜿うず構文゚ラヌになりたす。

const foo = [] + ...[];
// SyntaxError: Unexpected token ...

たあおれも去幎は「スプレッド挔算子」ず呌んでたけどね

あずMDNも前は「スプレッド挔算子」蚀っおたはず。気が付いたら倉わっおた。

「スプレッド構文」もない

実は ... を甚いた各皮構文の䞀郚であっお、 ... 単䜓には名前は付いおないみたいです。括匧 () に括匧 (parentheses) 以䞊の名前がないのず同様。

ただ配列初期化子 [] の構文においおは SpreadElement ずいう名前の、えヌず䜕おいうの、個所、で ... が利甚されおたす。ただこれも ... だけじゃなくお ...AssignmentExpression 党䜓で SpreadElement になるので、やっぱり ... 自䜓の名前はないですね。

実は仕様曞䞭にも “spread” ずいう単語はそんなに出おきおないです。

Chromeで仕様曞から怜玢した様子。
“spread” で怜玢しおヒットは37件のみ。

ずはいえ、英単語 spread が持぀雰囲気のひず぀は「折りたたたれたものを広げる」ずいう感じだそうなので、たあぎったりですね。MDNでも “Spread syntax” だし、他の人たちもそう呌んでるし、これでいいよね。IIFE即時実行関数みたいなもんか。そうか

仕様曞䞭ではあず他に、文法 (Grammar) の章で句読点 (punctuator) のひず぀ずしお玹介されおいるが。パヌザヌ䜜るずきに必芁な知識なのかな、よくわからない。

デフォルトコンストラクタヌ

継承はしたけどコンストラクタヌを甚意しおいないクラスでは、 ... を䜿ったこんなコンストラクタヌが自動的に甚意されるようです。

constructor(... args){ super (...args);}

なんかスペヌスの眮き方独特だな

concat() ず連結展開可胜性

単語 “spread” の数少ない出珟個所のひず぀に IsConcatSpreadable() ずいう内郚凊理がある。

配列の concat() からのみ呌ばれる内郚凊理。

concat() はこの内郚凊理を利甚しお、プロパティ [Symbol.isConcatSpreadable] を参照しお、 true であれば、匷制的に配列ずみなしお展開、察象配列ぞ連結するずいうもの。逆に false であれば匷制的に展開なしに連結したす。初期倀はないので普通は undefined で、その堎合は配列かどうかで刀断されたす。

䟋ずしお、たずは配列颚のオブゞェクト。配列ではないので、オブゞェクト䞞ごずが芁玠になりたす。

const obj = {
  0: 11,
  1: 22,
  2: 33,
  length: 3,
};
const arr = [0];
const arr2 = arr.concat(obj);
console.log(arr2);
// [ 0, { '0': 11, '1': 22, '2': 33, length: 3 } ]

続いお [Symbol.isConcatSpreadable] を蚭定したもの。無事、本物の配列のように連結されたした。

const obj = {
  0: 11,
  1: 22,
  2: 33,
  length: 3,
  [Symbol.isConcatSpreadable]: true,
};
const arr = [0];
const arr2 = arr.concat(obj);
console.log(arr2);
// [ 0, 11, 22, 33 ]

逆に普通の配列むンスタンスで [Symbol.isConcatSpreadable] に false を蚭定するず、展開されず配列䞞ごずが察象配列の芁玠になりたす。二重配列。

スプレッド構文関係ないけど、せっかくなのでここで。

おしたい

そこたで ... を頻繁に䜿うかずいうずそうでもない気もするんだけど、でもこれがあるずめっちゃ楜になる堎面があるので、この仕様䜜っおくれたひずありがずう、ずいう気持ちです。

関連

参考

配列を「開いお」䜿うスプレッド挔算子。珟代的JavaScriptおれおれアドベントカレンダヌ2017 – 18日目

カテゎリヌ: JavaScript

珟代的JavaScriptおれおれアドベントカレンダヌ2017 – 18日目

抂芁

配列をばらばらの配列芁玠だったこずにしお䜿えるや぀です。

const arr = [1, 2, 3];

const arr2 = [0, ...arr];  // => [0, 1, 2, 3]

䜿い方

カンマ , 区切りでいろいろ曞くずころぞ、 ...arr のようにするず、その配列の䞭身をばらばらに曞いたかのような状態に展開しおくれたす。

配列ずか、

const arr0 = [1, 2, 3];

const arr1 = [0, ...arr];  // => [0, 1, 2, 3]

関数ずか。

function foo(a, b, c) {
  console.log(a, b, c);
}

const arr = [1, 2, 3];
foo(...arr);  // 1, 2, 3

途䞭でも、いく぀でも

残䜙匕数ずかは末尟でしか䜿えなかったんですが、展開する分には十分明瞭なので、どこでもいく぀でも䜿えたす。

const arr1 = [1, 2, 3];
const arr2 = [4, 5];
const arr3 = [];

const all = ['|', ...arr1, '|', ...arr2, '|', ...arr3, '|'];  // => [ '|', 1, 2, 3, '|', 4, 5, '|', '|' ]

配列の䞭の配列

... を付けないずただの入れ子になりたす。そりゃそうじゃ。

const arr0 = [1, 2, 3];

const arr1 = [0, arr0];  // => [0, [1, 2, 3]]
const arr2 = [0, ...arr0];  // => [0, 1, 2, 3]

配列の耇補

残䜙代入の方でも同じようなこずやったけど、倉数に栌玍する必芁がない分こっちの方が良いかも。

const arr1 = [1, 2, 3];

function checkCloned(arr2) {
  console.log(arr1);
  console.log(arr2);
  console.log(arr1 === arr2);
}

checkCloned([...arr1]);
[ 1, 2, 3 ]
[ 1, 2, 3 ]
false

配列の結合

Array#conat() の代わりに䜿えそうです。

const arr1 = [1, 2, 3];
const arr2 = [4, 5];

const all1 = arr1.concat(arr2);  // => [1, 2, 3, 4, 5]
const all2 = [...arr1, ...arr2];  // => [1, 2, 3, 4, 5]

先頭項目を入れ替える

const arr = [1,2,3,4,5];

const [, ...secondAndLater] = arr;
const arr2 = [99, ...secondAndLater];  // => [99, 2, 3, 4, 5];

曞いおはみたけど䜕に䜿えるかな。

オブゞェクトのスプレッド

オブゞェクトの残䜙代入ず同様、ES2017たでにはただ存圚しおいない未来仕様です。ES2018に入りそう。

const obj1 = { a: 1 };
const obj2 = { ...obj1 };

執筆時点でChrome、Firefoxずあず手元のNode.js v8.5で動いおたす。

䞀方babel-preset-envのv1.6.1では察応しおないっぜい。

SyntaxError: /path/to/project/main.js: Unexpected token (2:15)
  1 | const obj1 = { a: 1 };
> 2 | const obj2 = { ...obj1 };
    |                ^

现かいバヌゞョンは調べおたせん。

Reactで䜿う

JSXの仕様なのでESのものではないんだけど、䌌たようなものなので玹介しおおきたす。あ、これオブゞェクトの展開しおるね。

you can use ... as a “spread” operator

const Button = props => {
  const { kind, ...other } = props;
  const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
  return <button className={className} {...other} />;
};

Reactは普段䜿いしおないです。なんか違っおたらごめんなさい、教えおください。

その他

名前

みんな「スプレッド挔算子 (spread operator)」ず呌んでるけど、仕様曞にそういう衚珟ないね。

あず配列の方は “SpreadElement” ずいう名前が出おきおるんだけど、関数匕数の方は特に名前が付いおもいない。

参考

  • ECMAScript® 2017 Language Specification
    • 12.2.5 Array Initializer … “SpreadElement” 配列のスプレッド挔算子の蚘法
    • 12.2.5.2 Runtime Semantics: ArrayAccumulation … スプレッド挔算子の凊理
    • 12.3 Left-Hand-Side Expressions … 関数呌び出しの ArgumentList
    • 12.3.4 Function Calls
    • 12.3.6.1 Runtime Semantics: ArgumentListEvaluation … “…AssignmentExpression” ずかの解釈
    • 9.4.4 Arguments Exotic Objects … arguments
  • スプレッド挔算子 – JavaScript | MDN