🦭

JavaScriptの代入をちゃんと理解する

2023/12/01に公開

はじめに

こんにちは。新卒エンジニアのwinnieです🐥

株式会社アクティブコア 初代アドベントカレンダーの記念すべき1日目を飾るこの記事ですが、前日の11月30日23時現在、絶賛執筆中でございます。

今日で11月が終わり、明日からはもう12月ですね。2023年もあっという間でした。

記事の作成が絶望的に間に合わないので、過去の下書きを掘り起こし中です。分かる人にとっては当たり前の内容ですが、Python使いだった自分が当時混乱した内容を記事にしました!

現象

いきなりですが、このコードの実行結果にとても困惑しました。

main.js
console.log(a = 1)  // 1

Pythonからプログラミングに入門した自分が想定していたのは、このような結果です。

main.py
print(a = 1)  # TypeError: 'a' is an invalid keyword argument for print()

両者は、代入という行為が値を返すか否かという点において異なります。Pythonでは値が返されずにエラーが発生する一方で、JavaScriptでは値が返されるためエラーは発生しません。

JavaScriptの経験が浅くPythonがプログラミング言語のすべてだった自分にとって、代入しただけで値まで返される(出力までできる)挙動はとても不思議に感じました。

結論としては、Pythonの代入が基本的に「文」なのに対し、JavaScriptの代入が「式」だったことが理由です。以下で詳しく説明していきます。

解説

大前提として、(おそらく大抵の)プログラミング言語には「式」と「文」があります。「式」と「文」の大雑把な違いは、値を返すか返さないかです[1]。値を返すのが「式」、値を返さないのが「文」になります。

main.js
1 + 2  // 式
const a = 1 + 2;  // 文
main.py
1 + 2  # 式
a = 1 + 2  # 文

ここで、Pythonの代入は基本的に「文」となります[2]。そのため a = 1 + 2 自体が値を返すことはなく、以下のコードは実行できません。

main.py
print(a = 1 + 2)  # TypeError: 'a' is an invalid keyword argument for print()

ところが、JavaScriptの代入は「式」になります[3]。つまり a = 1 + 2 自体が値を返し、以下のコードは実行されます。

main.js
console.log(a = 1 + 2)  // 3

JavaScriptの = は代入演算子と言い、代入という副作用を持ちつつ、式の結果を評価することで値を返します。つまり上記では 1 + 2 が評価されて 3 が返却され、その結果は a に代入されます。代入演算子の振る舞いについては以下の引用が分かりやすいでしょう[4]。代入自体は「式」ですが、行全体では「文」であるという点に注意してください。

main.js
// 変数 x を宣言し、 f() の返値で初期化します。
// x = f() の代入式の返値は破棄されます。
let x = f();

ここまでが、PythonとJavaScirptの代入に触れて混乱した原因になります。現象の理由はとても単純で、言ってしまえば言語仕様の違いです。ただ当時の自分は、代入1つをとってここまで大きな仕様の違いがあることを想像だにしていませんでした。また、そもそも「文」と「式」をちゃんと理解していなかったことも、大きな理由の1つだと思います。

余談

Pythonの代入は基本的に文だと述べましたが、Python 3.8からは代入式もサポートされるようになっています。「セイウチ演算子」と呼ぶそうです🦭[5]

main.py
print(a := 1)  # 1  # 言われればセイウチに見える…?
print(a)  # 1

ところで、ほかの言語も気になったので少し試してみました。C言語の代入は「式」のようですね[6]

main.c
int a;
printf("%d", a = 1);  // 1

Javaの代入も「式」でした[7]

main.java
int a;
System.out.println(a = 1);  // 1

Golangの代入は「文」みたいです。

main.go
var a int
fmt.Println(a = 1)  // syntax error: unexpected = in argument list; possibly missing comma or )

一瞬「おっ」と思って結局「式」ではなく「文」だったのですが、Golangは if 文の中に「文」を書けるみたいですね[8]

main.go
// aに1を代入したあと、aと1を比較している。
if a := 1; a == 1 {
  fmt.Println("1")  // 1
}

(Golangではいかにも書けなさそうですが)もし代入式があったらどうなるのでしょうか?いかにも書けそうな(ただし3.8までは書けなかった)Pythonで書いてみましょう。

main.py
# 代入と評価を同時に行う
if a := True:
    print(a)  # True

もちろんJavaScriptでも。

// 代入と評価を同時に行う
if (a = true) {
  console.log(a)  // true
}

代入式、便利ですね!

おわりに

というわけで、なんとかアドベントカレンダー1日目の記事を書き終わりました。25時を回ってしまいましたが、カレンダーの公開までは6時間もあるので問題ありません。初回がこんなですが、ほかのメンバーも続いてくれることを祈っています🙏

自分もこの記事含め、全部で5記事以上は書くつもりです!それでは最後に、カレンダー2日目の紹介をして締めようと思います。

明日のカレンダーは undefind です!お楽しみに!(激寒)

脚注
  1. プログラミングにおける式と文とは - 愚鈍人 ↩︎

  2. 7. 単純文 (simple statement) — Python 3.12.0 ドキュメント ↩︎

  3. 代入演算子 - 式と演算子 - JavaScript | MDN ↩︎

  4. 評価とネスト - 式と演算子 - JavaScript | MDN ↩︎

  5. What's New In Python 3.8 — Python 3.12.0 ドキュメント ↩︎

  6. C言語 | 代入演算子 ↩︎

  7. Java | 代入演算子 ↩︎

  8. else ifにも代入文が書ける #golang #Go - Qiita ↩︎

GitHubで編集を提案
株式会社アクティブコア

Discussion