JavaScriptの奇異なSyntax

 
Web Tips.
Booskanium's
Booskanium's Web Tips.

一見して、奇異なコーディングもJavaScriptたる所以です。

JavaScriptの柔軟すぎる構文を理解しておかないと記号のお化けにしか見えないケースがあります。
そこで、リバースエンジニアリングの為に知っておいた方が良いメモです。
プログラミングに限らず妙な新語や方言が好まれるのは世の常、でも過ぎたる方言は人に伝わりません。

セミコロンを書く、書かない

自分はセミコロンを書く派(習慣として染み付いている)ですが、書かないほうが読みやすい様な気もします。
しかし改行時のセミコロン自動挿入の有無を理解せずに、生半可にセミコロン無しで書くとミスを誘発します。
それと、JavaScriptの圧縮ツールでセミコロン自動挿入に対応していない物があり、改行が失われることよる誤動作や構文エラーが誘発するケースもあります。

関数の閉じかっこにはセミコロンを書かない

この理由はJavaScript構文で関数の閉じカッコに後ろにはセミコロンなしを推奨しています。この理由を何かの文献で読んで納得したのですが、その所在と理由は失念しました。

function func() {
	a,
	b,
};
    ↑関数の場合はこの閉じセミコロンを書かない

ただし、関数定義の後に“(”で始まる即時実行関数定義がある場合は思わぬ結果を招きますので、無駄でも関数の終わりにセミコロンを書く癖をつけた方が良いと思われます。

var func = function() {
	//...
}
(function() {
	//...
})());
これ、1つ目の関数が即時実行関数と解釈されます。 下記の様に書けば問題有りません。
var func = function() {
	//...
}
(function() {
	//...
})();

returnでオブジェクトを戻したい

returnでオブジェクトを戻したいときに

return
{
	a,
	b,
}	
							
駄目ですね、これは以下と同義です。
return;
{
	a,
	b,
}
							
正しくは
return {
	a,
	b,
}
							
と書かねばなりません。

あれれ、即時実行関数でクロージャーを書きたい

これ即時実行にならない

a = b + c
(function {
	処理
})()
							
無名関数のクロージャーにしたいのでしょうが、cの次の行の(が結合されて「c()」で関数呼び出しになります。
正しくは
a = b + c
!function {
	処理
}()
							
と書けば無名関数のクロージャーになります。
クロージャーと書きましたが、クロージャーが何故必要か?
それはプレイベート変数で処理したい場合に用います。JavaScriptの理解が難しい所以です。

詳しくはhttps://blog.tai2.net/automatic_semilocon_insertion.htmlを参照。

左かっこは改行前、それとも改行後

これは前述のセミコロンを書く・書かないの項で答えがでています。

問題がになるケースです

改行前に書く場合はなんの問題もありません。

return {
	a,
	b,
}
							
改行後に左かっこを書く場合と問題が発生するケースです。
return
{
	a,
	b,
}
							
この様な凡ミスに繋がりますので、左かっこは改行前に書く癖をつけて、目を慣らしといた方が良いです。

びっくりびっくり(!!)っ何?

リバースエンジニアリングしていると、時々目にする二重否定って何なんだろう。調べたら未定義変数にundefinedが戻らなかった初期の頃に活躍した名残みたいです。

比較演算子で活躍した名残

varなどで未定義項目にboolean型のfalseを戻してエラーにならない様にする

if (!!conditions) {
}
							
否定演算子は渡されたオブジェクトをboolean型の値で戻します。もし未定義のオブジェクトが渡されるとboolean型のfalseが戻ります。
今のJavaScriptエンジンは未定義項目にundefinedを返し、条件式としてはfalseになる仕様なので、この様な書き方は不要です。

昔のシーケンス処理的(Goto)な処理

JavaScriptにはGoto文がありません。Goto乱用でとんでもなく遠方にかっ飛ぶ奇っ怪なロジックが書かれてしまう事は防がれています。しかし単純なシーケンス処理を表現したい場合は不便です。
そこで単純なシーケンス処理的な事を行いたい場合は、labeled文にcontinueとbreakを組み合わせる方言記述が行われます。この方言で閉じられた階層内や親階層への遷移ができます。

参考になる文献

JavaScriptでgoto文的な処理ができるlabeled文の使い方とコード例
[JavaScript] goto 文の実装例

変数の巻き上げ

var variable  = "global";
function func() {
    console.log(variable);    // undefined
    var variable = "local";
    console.log(variable);    // local
}
func();
							
上記のコードの巻き上げって、なんというムフフな構文なんでしょう。
最初のconsol.logでvar定義前だからグローバル変数が参照されると思っていると痛い目にあいます。
なお、巻き上げはアロー関数と関数リテラルでは起きません。これもなんともムフフです。

このムフフな構文の落とし穴にはまらない様にするために、
・関数内のクロージャ変数は最初にまとめて書く
場当たり的な変数乱用の前に変数を考える癖もつきます。

オブジェクトの複製方法

これ、知らないと思わぬ不具合を招きます。

これ、複製じゃなくて参照です

「obj2 = obj1;」

オブジェクトの複製方法は

「obj2 = JSON.parse(JSON.stringify(obj1));」
※Date、関数、Undefined などのいくつかのプリミティブ/オブジェクト型がオブジェクトに含まれているとうまくいかない。

「obj2 = structuredClone(obj1);」
※オブジェクトに関数を含むとエラーになる。

「obj2 = structuredClone(obj1);」
※オブジェクトに関数を含むとエラーになる。
詳しくはhttps://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone#transferring_an_object

これ、表層のオブジェクトのみの複製

「obj2 = Object.assign({}, obj1);」
これ、階層を持つオブジェクト複製には使えない。

JavaScriptのメモリー利用

カンマ演算子

理解していないとリバース・エンジニアリングに支障がでます。
おさらい:
(,)」は、それぞれの演算対象を「(左から右に)」評価し、()内の最後のオペランドの値を返します。
※赤文字部分も構文の一部です。
()に注目する!!

hoge = ("abc","edf","ghi");
※hogeには"ghi"が戻ります。

hoge = "abc","edf","ghi";
※hogeには"abc"が戻ります。

ピリオド3っつ

「...」 これ三点リーダーに見えますが、ピリオド3つです。
ピリオド3つは、配列やオブジェクトの「[ ] や {}」を外して複数の値にする。
知らないと記号のお化けです。
知っていると簡素な表現ができる場面があります。知らないと難読に^^;

let a = [1, 2, 3]
console.log(a) // [1, 2 3]
console.log(...a) // 1 2 3

用途:配列のデープコピー&マージ
let a = [1, 2, 3]
let b = [4, 5, 6]
let c = [...a, ...b] //デープコピー&マージ
console.log(c) // [1, 2, 3, 4, 5, 6]

ドットつなぎ

オブジェクト思考のプログラミングなら一般的に用いられる記法です。
オブジェクトの戻り値をつなぎます。
これを理解していれば、Promiseオブジェクトをコールバック関数のthen()でつなげる記法がスムーズに理解できます。

変数名にハイフン

ハイフンを含む変数名への参照で「abc-xyz」はエラーですが、「親オブジェクト['abc-xyz']」で参照できます。
JSONやXMLのメンバー変数名にハイフンが利用されていることが多々あることから知っておく必要があります。

const abc = {
  "xyz-123" : "変数にハイフン"
}
console.log(abc['xyz-123']);		//OK
console.log(abc.xyz-123);		//ERROR

document直下の変数名にハイフンを利用する場合、おすすめは出来ませんけれど。

document['a-b'] = "変数にハイフン";
console.log(document['a-b']);		//OK
console.log(a-b);		//ERROR