背景:プログラミングを学ぶ人の支援活動として、JSの学習支援を行うことになった*1
課題:正直に言うとJSがあまり好きではなく、避けてきた。正しく理解できていない。最低限説明できるレベルまで理解する必要あり
アプローチ:別件でIoT教材の再実装が必要なのだが、JSを使って実装することで開発を通じて言語仕様を学ぶ。また、JS解説本を買って一通り読む
詳細:
他人に提供する予定のソフトにおいてはできるだけ正しくコーディングしたいと思い、JSの理解のためにO'REILLYのJavaScriptを買った。最初本屋でこの本を見た時は分厚すぎて、これは買わないなーと思ってその時は買わずに帰ったのだが、JSでプログラミングを始めるといろいろ不明な点が出てきた。このJSには、コーディングスタイルとか、本来どのように実装するべきなのか?まで細かく説明してくれているので買う価値があると判断した。
クロージャ・・・・関数オブジェクトと関数の変数の名前解決に使われるスコープを組み合わせたもの
(関数が定義された時に有効であったスコープが変数スコープとして管理される(スコープチェーンへの参照))
クロージャのふるまいを理解するため、本のサンプルをベースに試作しました。nodejsで動作確認済み
#!/usr/bin/node let scope = 'global scope'; global_reporter = () => {return scope;} /////////////////////////////////////////////////////////////// console.log("-------- closure test No1 ----------"); function check_scope(){ let scope = 'local scope'; function f() { return scope; } return f(); // returns value of scope } console.log(check_scope()); // -> local scple console.log(global_reporter()); // -> global scppe /////////////////////////////////////////////////////////////// console.log("---------- closure test No2 ----------"); function check_scope2(){ let scope = 'local scope'; function reporter() { return scope; } function changer(msg) { scope = msg; } return [reporter, changer]; } const [rep, chg]=check_scope2(); console.log(chg); // -> [Function: changer] console.log(rep); // -> [Function: reporter] console.log(rep()); // -> local scope chg('local scope var is changed'); console.log(rep()); // -> local scope var is changed console.log(global_reporter()); // -> global scppe /////////////////////////////////////////////////////////////// console.log("---------- closure test No3 ----------"); function check_scope3(){ let scope = 'local scope'; return [() => { return scope; }, (msg) => { scope = msg; }] } const [get, set]=check_scope3(); console.log(get); // -> [Function] console.log(set); // -> [Function] console.log(get()); // -> local scope set('val of local scope is modified by set function'); console.log(get()); // -> val of local scope is modified by set function console.log(global_reporter()); // -> 'global scppe' /////////////////////////////////////////////////////////////// console.log("---------- closure test No4 ----------"); function check_scope4(scope){ return [() => { return scope }, (msg) => { scope = msg}] } const [f1,f2]=check_scope4(true); console.log(f1); // -> [Function] console.log(f2); // -> [Function] console.log(f1()); // -> true f2('var of local scope is modified by set function(test4)'); console.log(f1()); // -> var of local scope is modified by set function(test4) console.log(global_reporter()); // -> global scppe
実行結果は以下。一応理解した通りの結果にはなっている。今後もっと難しいコードに出くわすかもしれないが、、今の段階では上記コードと実行結果は腑に落ちている。
-------- closure test No1 ---------- local scope global scope ---------- closure test No2 ---------- [Function: changer] [Function: reporter] local scope local scope var is changed global scope ---------- closure test No3 ---------- [Function] [Function] local scope val of local scope is modified by set function global scope ---------- closure test No4 ---------- [Function] [Function] true var of local scope is modified by set function(test4) global scope
曲解しがちな自分の反省
Pythonに最初に出会った時、インデントでスコープが決まると知ってキワモノ言語と曲解して使うのを避けていたのですが、必要に迫られてPythonを使いだしてその簡潔さ圧倒されました(そしてそれ以来、速度が問われないプログラムは全部Pythonになりました)。
JSに対しても、勉強を始めた当時JSの文法が納得できず*2、また、「JSとはWeb画面でダイアログ出したりDOM操作用の言語」と曲解しており、苦手意識もあって強く避けてきました。ですが、この本を読んで、それは古い時代のJSであり、ES2015(ES6)で仕様が美しくなり、毎年言語仕様が見直されるぐらいにホットな言語であると再認識しました。仕様の曖昧性が気に入らなかったら、strictモードで厳しく叱ってもらうとか、AltJSとしてTypeScriptを選ぶという手段もあるわけで・・・・
この本のお陰で、自分のJSに対する認識が10年以上前から止まったままと認識しました。ちゃんと勉強しなおすと、(実際はTypeScriptを使うかもですが) JSがかなり好きになりました。ただ、、thisのワナとかまだまだ手ごわい所もありそうです。JSの強みを活かしたプログラミングのためには、非同期処理も正しく理解しないとなとは思っています(当分その局面にはならないだろうけど)
■追記
JSの===(厳密な等価性)とは何なのか??
pythonのisはオブジェクトIDの一致を確認する。sliceで切り出した文字列と別の変数の文字列では同じ値でもオブジェクトのIDが異なるのでisによる判定ではFalseとなる。これはまぁ分かる
x = "aaa" msg = "asfsdfasaaasadfsdfsadf" msg[8:11] # -> 'aaa' x == msg[8:11] # -> True x is msg[8:11] # -> False id(x) # -> 123145299899312 id(msg[8:11]) # -> 123145299900016
JSの===は何に基づき比較する??
x = "aaa" msg = "asfsdfasaaasadfsdfsadf" msg.slice(8,11) // -> 'aaa' msg.slice(8,11) === x // -> true
MDNのサイトから引用
=== による厳密な等価性
厳密な等価性は、2 つの値が等しいか比較します。比較対象の値はどちらも、比較する前に別の値へ暗黙のうちに変換されることはありません。値が異なる型の場合、それらの値は等しくないとみなします。値が同じ型で数値ではない場合、同じ値であれば等しいとみなします。
まるめると、、===による比較とは、オブジェクトIDやオブジェクトのアドレス等による同一性確認ではなく、型変換しない==であると理解 (PythonのisやLISPのeqではないと)