0
0

JavaScript: let, const, classに関して用途以外の豆知識

Last updated at Posted at 2024-05-05

まず有効な記述法について。scopeが異なるので以下の例では2重定義とはなりません。

//外側block scope
let a=-~-~0-~-~0;
const b=1e+999;
//内側block scope
{
	let a=new class{};
	const b=class{};
	class c{}
}

などといった芸風はよく見掛けますね。しかし以下のような記述をすると不思議な事が起こります。

eval("let a=0;const b=1;class c{}");
console.log(typeof a, typeof b, typeof c)

evalの中には{}が無いのでabcはconsole.logと同一scopeに存在しているかのような印象を受けますが、実際にはeval実行部分でしか参照できません。よってconsole.logではundefinedが出力されます。
以下の記述は{}を省略できる状況でvarletによる変数宣言やclass定義を試みています。しかしなんとvar以外は文法違反です(当たり前田)。evalみたいな事ができません。

if(1)var a=0;
if(1)let b=0;
if(1)class c{}

setTimeoutを使うと何やら様子がおかしいですよ…。

{
	setTimeout("let a=0",0);
}
setTimeout(_=>console.log(a),1000)

何と変数aは外側のscopeに解き放たれています。結果として0が出力されます。そして驚くべき事に、block scope内の関数scopeでsetTimeoutを実行しても変数は最も外側のscopeに転がっていきやがります。

{
	let f=()=>{setTimeout("let a=0",0)};
	f()
}
setTimeout(_=>console.log(a),1000)

当然setIntervalでもその挙動は同じ事になります。以下の例だと即座にIdentifier 'a' has already been declaredなどとError警察からの通知が届きます

setInterval("let a=0",500)

以下の例は通常は文法違反となる2重定義。

f=a=>{let a=0}//引数の名前と被ってますよ

しかしsetTimeout様の手に掛かれば見事違反を貫通。

f=a=>{setTimeout("let a=0")}

では問題。以下の処理ではconsole.logで何が出力されるかな? 今までの説明で楽勝でしょう。

f=a=>{
	setTimeout("let a=0");
 	setTimeout(_=>console.log(a),1000)
}
f(1)

ちなみに最外殻scopeの汚染を防ぐには"{...}"のように処理を囲めばよい。

setTimeout("{let a=0}")

余談にはなりますがsetTimeoutの第一引数に文字列を渡した場合、その中では大域変数と最外殻scopeの変数しか参照できません。block scopeや関数scopeの変数を参照したければ第一引数に関数を渡す事になります。

{
	let a=0;
	setTimeout(_=>console.log(a))
	setTimeout("console.log(a)")//error
}

以上の事からsetTimeout("...")の文字列中の処理は最外殻scopeに記述しているのと同じ事かもしれません

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0