はじめに
普段C#で開発を行っていますが、最近Web開発技術も勉強中です。
その中で、C#とJavascriptで概念が違って混乱したことがあったので整理します。
==と===
C#では、値が等しいかどうかに==を使います。値型であれば、単純に値同士に比較をしてくれますし、参照型であればインスタンスが同じであるかで比較してくれます。
しかし、Javascriptでの==は意味が違うようでした。
例えば、以下のような場合、trueが返ってきます。
var a = "1";
var b = 1;
console.log(a==b);
結果:true
今見ても不思議な感じがしてしまいますが、文字列の1と数値の1は等しいとみなされるようです。
等価演算子 (==) は、二つのオペランドが等しいことを検査し、論理値で結果を返します。厳密等価演算子とは異なり、オペランドの型が異なる場合には型の変換を試みてから比較を行います。
数値と文字列を比較する場合、文字列を数値に変換しようとします。
とリファレンスにありました。ほかにもnullとundefinedの比較はtrue、オブジェクトと数値または文字列の比較の場合、オブジェクトを値型に変換して比較しようとするみたいです。詳しくはこちら。
Javascriptでの==は同じ型に変換を行ってから比較する場合があるため、意図せずtrueになってしまいバグを埋め込んでしまいそうです。
===で比較をすれば、型変換は起きず、数値の1と文字列の1を違うものとみなしてくれます。
C#の==と同じ感覚で使うのであれば、===を使った方がよさそうだとわかりました。
varとlet
C#では、変数を置く際にvarをよく使いますが、JavascriptのvarはC#とは異なる挙動をするようです。
varで同じ変数名で再度宣言することが可能で、しかも、それは同じ変数として扱われます。C#の場合、同じ変数名を使用すると構文エラーになりますが、Javascriptは大丈夫なようです。
また、Javascriptでは変数の巻き上げというのが起こり、varの場合、グローバルにあるいは関数内に定義していればその変数を定義より前で使うことができるようです。(関数内での宣言であれば関数内のみで使用可能。)
ただし、初期化は巻き上げられないため、初期化前にアクセスすると、varの場合はundefinedが返ってきます。
varで宣言した時点でundefinedで初期化されると考えてもいいのか(?)と思っています。
console.log(x); // undefined 変数の定義の巻き上げが起こるのでエラーにはならないが、
// 初期化は巻き上げられない。
var x = 1;
console.log(x); // 1
console.log(y); // ReferenceError yは関数の中なので、外から参照できない。
function foo() {
var y = 2;
console.log(y); // 2
console.log(x); // undefined 下で宣言しているので巻き上げがおこる。(グローバル変数でなく関数内のローカル変数を参照する) 未初期化のため undefined となる。
var x = 3 // 最初に宣言したxと同じ変数として扱われる。
}
foo();
console.log(x) // 3。関数の中でvarと宣言しなおしても変数名が同じであれば同じ変数扱いになる。
Javascriptではvarの他にletやconstでも変数を宣言できます。constは一回だけ宣言、代入ができる変数で、そこまでややこしくないので割愛します。
letは、変数の有効範囲がブロックの中のみという点がvarと異なります。
function varTest() {
var x = 1;
{
var x = 2; // 同じ変数
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
console.log(x); // ReferenceError letも変数の定義の巻き上げが起こる。
// また、同じ変数名であるがブロックの中で再度宣言されており、ブロックの外とは異なる変数扱いとなる。
// しかし、letの場合はundefinedで初期化されないためエラーとなる。
let x = 2; // 異なる変数として宣言される。
console.log(x); // 2
}
console.log(x); // 1。{}(ブロック)の中と外では、同じ変数名を宣言していても、異なる変数として扱われる。
}
varは再宣言が可能なので、意図せず同じ変数名を使った場合に気が付けなさそうだと思いました。
letはブロックが異なれば、同じ変数名を使えはしますが異なる変数として扱えるので意図せず値を代入することは無さそうです。
C#と同じ感覚で使えそうなのはletでしょうか。varだと再宣言できる点がミスにつながりそうで怖いです。
巻き上げがあるという意味ではどちらもC#に無い概念ですが、letであればエラーになるため、まだわかりやすそうです。
さいごに
C#しか知らないので、慣れるまでは混乱して間違えそうだと思いました。
特にvarとletは記事にまとめてみようとすると、すごく混乱してしまいました。わかったようでわかってなかったなと思いました。
間違えてそうなので教えてもらえるとありがたいです。