あなたの書いた
JavaScriptコードは
どのように動いているのか
2020/04/30
古川 亘 / HN: 古都こと
フロントエンドエンジニア
@株式会社トップゲート 大阪事業所
ゲーム / プログラミング
TypeScript / React / React Native / Rust / Go
趣味:
技術:
ブログやってます
今日話す内容
(function main(t) {
[...t].forEach((d) => console.log(summarize(d.innerText)));
})(document.querySelectorAll('div'));
↑
こういうわけわからんのを
倒す!!!!!
プログラムは
命令の列挙
const a = 1;
const b = 2;
const c = a + b;
console.log(c);
プログラムは
命令を並べただけ!
const a = new Array(5).fill(0).map((_, i) => i);
🙂
const a = 1 + 2;
🤨
🤪
const x = ((a) => (b) => a + b)(1)(2);
ほんとぉ?
もうちょっと
掘り下げていこう
式と文と評価
コードの解釈フェーズ
字句解析(Lexical Analysis)
構文解析(Syntax Analysis)
意味解析(Semantic Analysis)
細かい処理挟みつつ
バイトコードや機械語に
各フェーズの説明
- 字句解析(Lexical Analysis)
- コードを記号や識別子などのトークンに分解する
- 構文解析(Syntax Analysis)
- 得られたトークンの並びを文法的に解釈
- 意味解析(Semantic Analysis)
- 文法は正しくても意味は正しいか?
- そのキーワードはそこで使えるか?型は正しいか?
各フェーズの説明
-
字句解析(Lexical Analysis)
- コードを記号や識別子などのトークンに分解する
-
構文解析(Syntax Analysis)
- 得られたトークンの並びを文法的に解釈
-
意味解析(Semantic Analysis)
- 文法は正しくても意味は正しいか?
- そのキーワードはそこで使えるか?型は正しいか?
JavaScriptの構文
-
「式(Expression)」と「文(Statement)」
- リテラル式、二項演算式、関数式、関数呼び出し式
- 変数宣言文、関数宣言文、if文、for文、ブロック文
- などなど
- コードは「文」の組み合わせでできている
- 「文」の中に「式」が含まれることがある
- という認識で今のところはおk
- この「文」が俗にいう「命令」というわけ
const a = 1 + 2;
console.log(a);
式
文
式と評価
- 式は実行時に評価(evaluation)される
- 「計算」と言い換えるとわかりやすいかも
- 評価されると値になる
- 逆に「評価されるものが式」という覚え方でも可
- 評価の例
- 「1 + 2」は「3」に評価される
- 「console.log('Hi')」は「undefined」に評価される
- ※戻り値がないため
const a = 1 + 2;
const a = 3;
console.log(a);
undefined;
※console.logに戻り値はないためundefinedになる
式と式文
- コードは「文」の組み合わせ
- でも例外的に「式」は「文」になれる
- これを「式文」という
- 逆(文 → 式)は基本的に無理
const a = 1 + 2; // 代入文
1 + 2 + 3; // 式だけど文として扱える
console.log('Hi'); // これも式だけど文
式の評価順序
- 式の評価はひとつずつ順番に行われる
- 「1 + 2 + 3」という式
- まずは「3 + 3」に評価されてから「6」に評価
- 「1 < x < 5」が動かない理由がこれ
- 先に「1 < x」が評価されて「true < 5」になる
- JavaScriptでは左から右、上から下の評価順番
- 直感的でわかりやすいはず
- このあたりは言語による
評価戦略
- 正格評価(Strict Evaluation)
- 式が現れた時点で評価する
- 世のほとんどのプログラミング言語はこれ
- JavaScriptも正格評価
- 遅延評価(Lazy Evaluation)
- 評価はできるだけ先延ばしにする
- 評価しないと進行できなくなってから評価する
- Haskellなどはこれ
コードを「式」で認識する
- 式レベルでコードを読めるようになると世界が広がる
- このコードはどう動いているのか?
- 評価の順番は?
- 副作用の影響は?
- 新しい文法やAPIが導入されてもあまり混乱しなくなる
- 結局のところ式か文になるので
- どれだけ複雑なコードでも単純な式評価を繰り返すだけ
評価の例
const add = (a, b) => a + b;
console.log(add(1, add(2, 3)));
const add = (a, b) => a + b;
console.log(add(1, add(2, 3)));
四角で囲っているのが式
関数呼び出し式では
引数の式を左から順番に評価する
const add = (a, b) => a + b;
console.log(add(1, add(2, 3)));
今回はconsole.logの引数はひとつだけなので
add(1, add(2, 3)) を評価する
const add = (a, b) => a + b;
console.log(add(1, add(2, 3)));
addの引数は2つあるので左から評価する
1 → 1(リテラルなのでそのまま)
add(2, 3) → 評価する
const add = (a, b) => a + b;
console.log(add(1, add(2, 3)));
addの引数は2つあるので左から評価する
2 → 2(リテラルなのでそのまま)
3 → 3(リテラルなのでそのまま)
add(2, 3)からadd(2, 3)が得られるので評価する
const add = (a, b) => a + b;
console.log(add(1, 5));
add(2, 3)を評価すると5になる
add(1, 5)が得られる
これを評価する
const add = (a, b) => a + b;
console.log(6);
add(1, 5)を評価して6が得られる
console.log(6)を評価する
const add = (a, b) => a + b;
undefined;
console.log(6)を評価する
コンソールに6が出力される
console.log自体は何も返さないのでundefinedに評価される
評価の例 その2
const x = ((a) => (b) => a + b)(1)(2);
const x = ((a) => (b) => a + b)(1)(2);
関数式
関数呼び出し式
const x = ((b) => 1 + b)(2);
関数式
関数呼び出し式
const x = ((a) => (b) => a + b)(1)(2);
const x = 3;
関数式
関数呼び出し式
const x = ((b) => 1 + b)(2);
まとめ
まとめ
- コードは命令の列挙 → 文(or式文)の列挙
- 「式」と「評価」という概念
- 式は値に評価される
- 基本的に左から順番にひとつずつ評価
- 式レベルでコードを読めると理解が深まる
- 難しいコードも読めるようになるかも
- 「なんか知らんけど動いた」からの脱却
- 式に目を向けてみよう!
参考文献
参考文献
- ECMAScript® 2019 Language Specification
- 式と演算子 - JavaScript | MDN
- 文と式 · JavaScript Primer #jsprimer
あなたの書いたJavaScriptコードはどのように動いているのか
By Koto Furumiya
あなたの書いたJavaScriptコードはどのように動いているのか
「プログラムとは命令を書き並べた物である」と私たちは習いました。しかし現実には「命令=1行」として見るとどうにも腑に落ちないコードがたくさんあります。今日はそんな不思議なコードを支える裏側を覗いてみましょう。 完全に理解した人達の「Web技術」Talk #4 発表資料 https://easy2.connpass.com/event/173015/
- 1,840