スクリプト1
const obj = {
a: 123,
b: "abc"
}
let a = () => {} // 文1: とりあえず空の関数で変数初期化しているだけね
let b = () => {} // 文2: 同上
({a, b} = obj)
console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
a 123
b abc
正しく呼べているように見える
スクリプト2
const obj = {
a: 123,
b: "abc"
}
let a = null // 文3: とりあえず空の関数で変数初期化しているだけね
let b = null // 文4: 同上
({a, b} = obj)
console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
[stdin]:9
({a, b} = obj)
^
ReferenceError: Cannot access 'b' before initialization
at [stdin]:9:6
at Script.runInThisContext (node:vm:129:12)
at Object.runInThisContext (node:vm:307:38)
at node:internal/process/execution:79:19
at [stdin]-wrapper:6:22
at evalScript (node:internal/process/execution:78:60)
at node:internal/main/eval_stdin:30:5
at Socket.<anonymous> (node:internal/process/execution:195:5)
at Socket.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1359:12)
Node.js v18.16.0
変数初期化の値を変えるだけで動かなくなるの?謎。
スクリプト3
const obj = {
a: 123,
b: "abc"
}
let a = null
let b = null
b = null
({a, b} = obj)
console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
[stdin]:10
({a, b} = obj)
^
TypeError: null is not a function
at [stdin]:10:1
at Script.runInThisContext (node:vm:129:12)
at Object.runInThisContext (node:vm:307:38)
at node:internal/process/execution:79:19
at [stdin]-wrapper:6:22
at evalScript (node:internal/process/execution:78:60)
at node:internal/main/eval_stdin:30:5
at Socket.<anonymous> (node:internal/process/execution:195:5)
at Socket.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1359:12)
Node.js v18.16.0
これでもダメか・・・
答え合わせ
- セミコロンを置いていないので、スクリプト2では文4の後の文のカッコが関数呼び出しとみなされている
- その結果
null({a, b} = obj)という関数呼び出しのような形になった - カッコの中身が
nullの次に評価されて、それがbに代入されるという形になった - カッコの中身の評価中に
bが必要だがこれはまさに今宣言しようとしている値なのでinitialize前に使うなというエラーが出た
- その結果
- 一方でスクリプト1では構文解析器が文2の後に自動的にセミコロンを置いてくれた
分割代入にカッコが必要な理由
TS2809による
変数の分割代入を行い、かつその場で変数・定数宣言しない場合(つまりletやconstを使わずに分割代入する場合)は代入文にカッコが必要
let {a, b} = obj; // これはOK
{a, b} = obj; // これはNG
({a, b} = obj); // これはOK
セミコロンを置かなかった理由
ESLintのルールで、このプロジェクトではセミコロンありだと警告が出るようになっていた
上記2つが悪魔合体した結果、スクリプト2のようにぱっと見動作しそうだがなぜかエラーになる・スクリプト1のケースではなぜか動く という奇妙な状態になっていた
面白い挙動だったので備忘録として残しておく