PHPとJSの変数の違い
主に使う言語はphpだが、jsを書くこともしばしばありjsのルールを理解することがたまに難しいことがあったのでそれについてまとめてみました。
PHPとJSのバージョン
phpは今回の話題についてはバージョンの違いはあまりないと認識しているが、使用例に使う構文はPHP8.1で動作してます。
jsについてはES2015の内容で書いているつもりですが、ES2016以降に変わってて間違ってる場合もあるかもしれないです。
変数の取り扱いの違いについての話題なので、「そもそもそんな構文はナンセンスだよ」とかも今回はスルーしていきます。
変数宣言のキーワード
PHP
$hoge = 'test';
phpには変数宣言のキーワードは存在しない
JS
hoge1 = "hogehoge1"; // <= varと同意義
var hoge2 = "hogehoge2";
let hoge3 = "hogehoge3";
jsには存在し、宣言キーワードを使用しないで変数の宣言はできるがグローバルな変数として扱う
let _let = '宣言'; // ok
_let = '代入'; // ok
let _let ='再宣言'; // Identifier '_let' has already been declared
let
で宣言された変数は再宣言をしてはならない。
変数名のルール
PHP
- ドル記号の後に変数名が続く
- 大文字と小文字を区別する
- 文字またはアンダースコアから始めなければならない
hoge = 'test'; // PHP Parse error: syntax error, unexpected token "="
$hoge = 'lower';
$HOGE = 'upper';
$hoge !== $HOGE; // true
$hoge = 'hoge'; // ok
$_hoge = 'hoge2'; // ok
$1hoge = 'hoge3'; // Parse error: syntax error, unexpected integer "1", expecting variable or "{" or "$"
JS
- 大文字と小文字を区別する
- 半角のアルファベット、_(アンダースコア)、$(ダラー)、数字の組み合わせ
- 数字から始めてはならない
- 予約語と同一ではならない
let hoge = 'lower';
let HOGE = 'upper';
hoge !== HOGE; // true
let $hoge = 'hoge1'; // ok
let _hoge = 'hoge2'; // ok
let 3hoge = 'hoge3'; // SyntaxError: Invalid or unexpected token
let if = 'hoge4'; // SyntaxError: Unexpected token 'if'
変数名のルールは大きくは違わないが、変数宣言の有無があるので$で始まるという制約以外はphpは緩めな印象。
jsは変数名が$で始まるという大きな制約がない代わりに数字や予約語などを考慮しなければならない。
$hoge$fuga
なんて変数名もいけるが、phperにはむず痒い気がする。
また、どちらも変数名には「日本語」は使用できるが、
注意: ここで言うところの文字とはa-z、A-Z、128から255まで (0x80-0xff)のバイトを意味します。
これらに加えて、ひらがなや一部の漢字なども変数名に利用できますが、全角の文字列が混在すると環境によって扱いにくいこともあるためお勧めしません。
どちらも使うべきではないという認識で良さそう。
変数のスコープ
phpとjsの変数の大きな違いはやはりスコープという概念だと感じたのでここが今回の主題です。
phpにも「変数のスコープ」と「グローバル変数」があるが、やはり緩いというか他の言語と比べると緩い。
PHPにおける変数のスコープといえば
$var = 'hello world';
function say() {
echo $var;
}
say(); // Warning: Undefined variable $var
phpにとって変数のスコープといえば、関数の外と中で違うというのが最初に覚えることであり、それ以降あまり出てこない印象。
もちろんフレームワークなどを用いたりしたオブジェクト指向で組んでnamespaceなどの利用は全くこれに当てはまらないが、シンプルなコード単位での話。
JSにおける変数のスコープ
var _var = 'this is var';
function sayVar() {
console.info(_var);
}
sayVar(); // 'this is var'
let _let = 'this is let';
function sayLet() {
console.info(_let);
}
sayLet(); // 'this is let'
jsでは関数の外側で定義された変数は内側では参照することができる。
phpにとっては関数が境界線ってイメージだが、jsでは外側か内側かの考えを持たなければならない。
内側宣言=>外側参照
PHP
function makeVar() {
$funcVar = 'var';
}
makeVar();
echo $funcVar; // Warning: Undefined variable $funcVar
js
function makeGlobalVar() {
_global_var = 'global var';
}
makeGlobalVar();
console.info(_global_var); // 'global var'
function makeVar() {
var _var = 'var';
}
makeVar();
console.info(_var); // ReferenceError: _var is not defined
function makeLet() {
let _let = 'let';
}
makeLet();
console.info(_let); // ReferenceError: _let is not defined
phpでは関数内で宣言された変数は外側では参照できず影響しない
jsでは宣言キーワードが省略された場合、グローバル変数となり内側での宣言というスコープがなくなりどこからでも参照できてしまう変数となってしまう。これは意図してないコードの代表例でするべきではない。
ここまでは関数におけるスコープの違いを比較してみたが、jsにはブロックスコープというのも存在している
jsのブロックスコープ
jsには関数でのスコープ以外にブロックによるスコープが存在する
PHP
if (true) {
$var = 'in block';
}
echo $var; // 'in block'
ifブロック内で宣言された変数はブロック外から参照できる
JS
if (true) {
var _var = 'in block';
}
console.info(_var); // 'in block'
if (true) {
let _let = 'in block';
}
console.info(_let); // ReferenceError: _let is not defined
jsではvar
で宣言したものはブロックスコープを持たずグローバルになり、let
で宣言したものはブロックスコープを持つという性質がある。
if (true) {
let _let = 'in block';
console.log(_let); // => "in block"
}
console.log(_let); // ReferenceError: _let is not defined
let
で宣言した変数は宣言配下のスコープでしか参照できない
ブロックチェーン
ブロックが入れ子になってもスコープの概念は変わらない
{
let _let = 'scope';
{
console.info(_let); // 'scope'
}
console.info(_let); // 'scope'
}
console.info(_let); // ReferenceError: _let is not defined
参照する位置から近い順で変数を見つけるとい性質もある
{
let _let = 'scope 1st';
{
let _let = 'scope 2nd';
console.info(_let); // 'scope 2nd'
}
console.info(_let); // 'scope 1st'
}
phpだと'scope 2nd'
で上書きしてしまうという認識だがjsではブロックスコープがあるのでそれぞれのスコープ位置から参照を開始して保持するということができて便利
Discussion