【JavaScript】 今更だけど非推奨なwith構文を理解してみる
更新日:2023/12/21
JavaScriptにはwith構文というものがあります。
あまり聞きなれない構文ですが、使用することでコードを簡潔に記述できます。
ただし非推奨です。
オブジェクトのプロパティをスコープ解決に組み込む
with構文を使用すると、オブジェクトのプロパティをスコープ解決に組み込むことができます。
JavaScriptはlet、const、varなどで宣言した変数名の一覧を持っていて、コード上で変数名が現れるとその一覧を検索します。
しかしwith構文の中では、指定したオブジェクトのプロパティが先に検索されます。
with構文の使用例
次のコードはwith構文の使用例です。
with構文の例
const value = 5;
const obj = {
value:6
};
with( obj ){
console.log( value ); // 6
}
プロトタイプチェーンも対象
with構文は、プロトタイプチェーン上のプロパティも対象となります。
次のコードはプロトタイプチェーン上にプロパティを配置して、with構文で参照しています。
プロトタイプチェーン上のプロパティも対象
const value = 5;
const obj = Object.create({value:6});
console.log(obj); // {} ← 中身は空?
console.log(obj.value); // 6 ← プロトタイプチェーン上にある
with( obj ){
console.log( value ); // 6
}
複数のオブジェクトを指定
with構文は複数のオブジェクトを指定できます。
この場合、後から指定したオブジェクトが優先されます。
複数のオブジェクトを指定
const value = 5;
const obj1 = { value:6 };
const obj2 = { value:7 };
with( obj1,obj2 ){
console.log( value ); // 7 ← obj2が参照された
}
追記:2023/12/21
書き方が悪かったようで、間違い指摘いただきました。
(obj1,obj2)はカンマ演算子での演算です。最後に評価したものが演算結果になります。
例えば、a = (1,2,3,4) とすると、aの値は4です。
つまり(obj1,obj2)の結果はobj2なので、with( obj2 )と記述したものと同じです。
上記の例は「カンマで複数オブジェクトを指定できるけれどあまり意味が無いよ」という例でした。
with構文の利点と欠点
with構文は、実行速度の向上と低下という相反する利点と欠点があります。
利点:実行速度の向上
オブジェクトのプロパティ参照は、with構文を使用することで処理時間が短くなります。
次のようなwith構文を使っていないコードを考えてみます。
with構文を使っていないコード
const a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14;
let loop;
const obj = {
value:6
};
for( loop=0;loop < 10000 ; loop ++) {
obj.value ++;
}
console.log(obj.value ); // 10006
一行目は、変数が無数にある状態を意図しています。
この状況で forループ内の obj.value は、次の手順で検索されます。
- 変数一覧から、変数obj を検索
- obj 内から valueプロパティを検索
次に、with構文を使用したコードに書き換えてみます。
下のコードは該当部分だけ、抜粋しています。
with構文を使用したコード
with(obj){
for( loop=0;loop < 10000 ; loop ++) {
value ++;
}
}
今度は、次のような手順で value が検索されます。
- obj 内から valueプロパティを検索
変数一覧を検索しない分、検索速度が向上しているのがわかりますね。
この他の利点としては、長いオブジェクト名を何度も記述する必要が無くなるため、コードがスッキリするという点が挙げられます。
こちらを目的にwith構文を使用することもありそうですね。
欠点:実行速度の低下
with構文を使用すると、変数を検索するための処理時間が長くなります。
利点で挙げたwith構文を使用したコードを見てください。
ここでは変数loop が参照されています。
これは、次の手順で検索されます。
- obj 内から loopプロパティを検索
- 変数一覧から変数loop を検索
with構文を使用しない時よりも、検索速度が低下しているのがわかりますね。
この他の欠点は、obj内に loopプロパティがあったとき、不具合が生じるという点です。
不具合を回避するには、with構文で追加するオブジェクトの構造を熟知しておく必要があります。
自分で作成したものなら大丈夫だと思いますが、組み込みオブジェクトやAPIなどは資料として公開されてないプロパティがある可能性があるので、慎重に適用すべきです。
strictモードでは使用できない
with構文はstrictモードでは使用できません。
次のコードを実行すると、SyntaxErrorが発生します。
"use strict";
const value = 5;
const obj = {
value:6
};
with( obj ){
console.log( value ); // SyntaxError: strict mode code may not contain 'with' statements
}
exportやimport構文を使用するときや、class構文内などは強制的にstrictモードになります。
つまり、これらを使用するときはwith構文を適用できないのです。
with構文は非推奨
言語仕様上では特に言及していませんが、with構文がstrictモードでは使用できないことからも、排除したい意思を感じます。
MDNなどは非推奨と明言しているので、一般的にも非推奨と認識されているようです。
→ECMAScript2022でLegacy機能であることが明記されました
非推奨の理由は、with構文の欠点でも書いていますが、意図しない変数やプロパティを参照してしまうなどで、バグが発生しやすいことが挙げられます。
例えば次のようなコードがあるとします。
const obj = {
prop1:"a",
・・・多くのプロパティ
prop2:"b",
・・・多くのプロパティ
};
・・・数百行のコード
let prop2 = "c";
with(obj){
console.log( prop2 );
}
let prop2 = "c";で定義した変数を使用するつもりが、obj.prop2を使用しているというケースです。
場合によっては、気が付くのに非常に時間がかかる可能性があります。
また仕様変更で後からobjにprop2を追加することがあるかもしれません。
その際with構文を使用しているなら、同名の変数が使用されていないか全て洗い出す必要があります。
そうしないとオブジェクトにデータを追加しただけで、今まで動いていたものが不具合をおこす可能性があるのです。
とても怖いですね。
もう一つ、バグになりそうなコードを挙げておきます。
「あぶないな」と思えれば、JavaScript中級者です。
const obj = {
value:6
};
with(obj){
let value = 0;
for( loop=0;loop < 10000 ; loop ++) {
value ++;
}
}
Symbol.unscopables
[Symbol.unscopables]プロパティを使用すると、オブジェクトのプロパティをwith構文での検索対象から除外できます。
const obj = {
prop1:"a",
prop2:"b",
[Symbol.unscopables]:{
prop1:true, // 除外する
prop2:false // 除外しない
}
};
let prop1 = "c";
let prop2 = "d";
with(obj){
console.log( prop1 ); // "c" ←ローカル変数
console.log( prop2 ); // "b" ←obj.prop2
}
これを使用すれば細かい制御ができるのですが、「prop1はどこを見る?」「prop2はどこを見る?」と一つ一つ確認していく必要があるのはかわりありません。
とても面倒です。
obj.prop1と素直に記述したほうがわかりやすいですね。
長いオブジェクト名を記述するのが面倒なら
with構文の使用目的の一つに、長いオブジェクト名を記述するのが面倒ということがあります。
例えば次のようにネストされたオブジェクトを一つ一つ辿って記述していくケースなどです。
const obj = {
obj2:{
obj3:{
prop:"a"
}
}
}
console.log( obj.obj2.obj3.prop ); // "a"
with構文を使用すると簡潔に書くことができます。
with( obj.obj2.obj3 ){
console.log( prop ); // "a"
}
しかし上述の理由で非推奨ですし、そもそもstrictモードでは使用できません。
おススメは、次のようにオブジェクトを変数に代入することです。
const obj3 = obj.obj2.obj3;
console.log( obj3.prop ); // "a"
速度向上でwith構文は使用しない
僕のようなコンピューターの性能が低い時代からプログラマーをやっている人は、可能な限り処理速度を上げるクセが身についています。
そのため、with構文のような処理速度向上に役立つものは大好物です。
非推奨と言われても、しっかりと設計していけば不具合を回避できます。
しかしコンピューターの性能が爆上がりしている現在では、処理速度向上と言っても非常に微々たるものです。
欠点を回避しながらプログラミングする手間を考えると、ムダなような気がしてきますね。
更新日:2023/12/21
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。