[ノンプロ研]EffectiveJavaScript輪読会 #最終回

GAS

どうも。つじけ(tsujikenzo)です。もう、明日でクリスマスなんですね。今回は「ノンプロ研EffectiveJavaScript輪読会 #最終回」についてお届けします。

本記事は、わたしの所属しているコミュニティ、ノンプログラマーのためのスキルアップ研究会の『ノンプロ研 Advent Calendar 2021』 24日目の記事です。ノンプロ研では「働く力」を上げるというテーマを元に、様々な学びを得る機会を提供頂いております。私も少しでもお役に立てればと思って活動を続けております。

ノンプロ研 Advent Calendar 2021 - Adventar
プログラミング学習を行うノンプログラマーが集まる(メンバーによるカレンダーです。 ハッシュタグは #ノンプロ研アドベントカレンダー です。

ElementsJavaScript輪読会とは

2021年夏に、ノンプロ研の有志が集まり、こちらの書籍を読み解いていく(いわゆる輪読会)が企画されました。

集まったメンバーは、主にGAS中級講座卒業生や受講生で、10名弱の参加となりました。

輪読会の進め方

輪読会の進行は、このような感じです。

  1. ひとり1~2項目についてプレゼン(スライド有無など自由)
  2. 項目あたり10~15分まで ※項目による
  3. プレゼン後に時間があれば意見交換

だいたい1章が平均10項目なので、1~2回で1章分の輪読会が終わる感じです。

Effective JavaScriptならではの注意点

今回の輪読会メンバーは、GASを学んでいるメンバーなので、JavaScriptではなく、GASで読み替えをすることが条件でした。

なので、第7章はまるまるスキップされました。

  • JavaScriptなので、GASのスクリプトエディタで実行できるようにコードを読み替え
  • ES5なので、V8に読み替え
  • GASに関係ないトピックはスキップ

今回、特につらかったのが、GASの読み替えと、V8へのリファクタリング とテスト駆動開発です。

書籍は、JavaScriptのES5が最新情報として書かれていますので、ES6以降に導入された便利な構文などに読み替える必要がありました。

そして、すべてのコードが書かれているわけではないので、コードが動くような実装を書かないといけません。

イマジネーション能力が問われます。力が付きました。

開催日時

輪読会は、計8回でした。おおよそ2週間に1度開催されるイメージでした。

実際に開催された日程と、読み手担当は以下です。

第1回 2021年08月21日

項目内容担当
項目1どのJavaScriptを使っているのかを意識しよう橘乃吏子
項目2JavaScriptの浮動小数点数を理解しようつじけ
項目3暗黙の型変換に注意しようタカハシノリアキ
項目4オブジェクトラッパーよりもプリミティブが好ましいetau
項目5型が異なるときに ==を使わないこはた
項目6セミコロン挿入の限度を学ぼうYukihiro Yamaguchi
項目7文字列は16ビットの符号単位を並べたシーケンスとして考えようSWD

第2回 2021年09月08日

項目内容担当
項目8グローバルオブジェクトを使うのは、最小限にとどめるこはた
項目9ローカル変数は、かならず宣言しようタカハシノリアキ
項目10withは避けようスキップ
項目11クロージャと仲良くしようつじけ
項目12変数の巻き上げ(ホイスティング)を理解するYukihiro Yamaguchi
項目13ローカルスコープを作るには即時関数式(IIFE)を使おうSWD
項目14名前付き関数式のスコープは可搬性がないので注意しようetau
項目15ブロックローカルな関数宣言のスコープも可搬性がないので注意しようタカハシノリアキ
項目16evalでローカル変数を作らないスキップ
項目17直接evalより、間接evalが好ましいスキップ

第3回 2021年09月26日

項目内容担当
項目18関数、メソッド、コンストラクタの、呼び出しの違いを理解するなー
項目19高階関数を快適に使えるようになろうMasayoshi Kataoka
項目20カスタムレシーバ付きでメソッドを呼びだすにはcallを使うこはた
項目21いくつでも引数をとれる関数を呼び出すにはapplyを使おうSWD
項目22可変長引数関数を作るには、argumentsを使うつじけ
項目23argumentsオブジェクトを書き換えないetau
項目24argumentsへのリファレンスは変数に保存するタカハシノリアキ

第4回 2021年10月14日

項目内容担当
項目25固定レシーバを持つメソッドを抽出するにはbindを使うSWD
項目26関数をカリー化するには、bindを使うetau
項目27コードをカプセル化するには、文字列ではなくクロージャを使うこはた
項目28関数のtoStringメソッドに依存するのは避けようつじけ
項目29非標準のスタック調査プロパティを使うのは避けようMasayoshi Kataoka
項目30prototype、getPrototypeOf、__proto__の違いを理解するYukihiro Yamaguchi
項目31__proto__よりもObject.getPrototypeOfが好ましいタカハシノリアキ
項目32__proto__は決して変更しないことタカハシノリアキ

第5回 2021年10月31日

項目内容担当
項目33newに依存しないコンストラクタの作り方etau
項目34メソッドをプロトタイプに格納しようタカハシノリアキ
項目35プライベートデータの格納にはクロージャを使おうつじけ
項目36インスタンスの状態は、インスタンスオブジェクトにだけ保存するSWD
項目37thisの暗黙的な結合を理解しようこはた
項目38スーパークラスのコンストラクタは、サブクラスのコンストラクタから呼び出すMasayoshi Kataoka
項目39スーパークラスのプロパティ名は、決して再利用しないYukihiro Yamaguchi

第6回 2021年11月14日

項目内容担当
項目40標準クラスからの継承を避けるなー
項目41プロトタイプを「実装の詳細」として扱おうMasayoshi Kataoka
項目42やみくもなモンキーパッチを避けるMasayoshi Kataoka
項目43軽量ディクショナリはObjectの直接インスタンスから構築しようetau
項目44プロトタイプ汚染を予防するために、nullプロトタイプを使うSWD
項目45プロトタイプ汚染を防御するためにhasOwnPropertyを使うこはた
項目46順序を持つコレクションには、ディクショナリではなく配列を使おうタカハシノリアキ
項目47Object.prototypeには、列挙されるプロパティを決して追加しないつじけ

第7回 2021年11月28日

項目内容担当
項目48列挙の実行中にオブジェクトを変更しないetau
項目49配列の反復処理には、for…inループではなく、forループを使おうなー
項目50複数のループよりも反復メソッドが好ましいYukihiro Yamaguchi
項目51「配列のようなオブジェクト」にも、総称的な配列メソッドを再利用できるこはた
項目52配列コンストラクタよりも配列リテラルのほうが好ましいつじけ

第8回 2021年12月21日

項目内容担当
項目53一貫した規約を維持しようSWD
項目54undefinedは「値なし」として扱うなー
項目55オプションオブジェクトで、キーワード付き引数群を受け取ろうHassy
項目56不必要な状態を排除するYukihiro Yamaguchi
項目57柔軟なインターフェイスのために、構造的な型付けを使うSWD
項目58配列と「配列のようなもの」を区別しようつじけ
項目59過剰な型強制を防ごうこはた
項目60メソッドチェーンをサポートしようetau

登壇データ

  • 8回 タカハシノリアキ、etau、こはた、SWD、つじけ
  • 6回 Yukihiro Yamaguchi
  • 5回 Masayoshi Kataoka
  • 4回 なー
  • 1回 橘乃吏子、Hassy

いま振り返っても、すごいですね。。。

輪読会で得た知見

「結局、どんなTipsが為になったのか」ここが、みなさんの、いちばんの関心ごとでしょう。

ランキング形式でお伝えします。

第3位 プロトタイプベース

JavaScriptはプロトタイプベースということが、少し理解できた気がします。

このような解説は、あまり書籍にも載っておらず、自分が理解できるまで、ひたすら考察しました。

function myFunction6_43_00_02() {

  //オブジェクトの生成
  const obj = { name: 'Tom' };

  //Objectオブジェクトから、Object.prototypeを継承している
  console.log(obj.__proto__ === Object.prototype); //true

  //Object.prototypeには、valueOf()や、hasOwnProperty()など、6つのインスタンスメソッドがある。(非推奨は除く)
  //objはObject.prototypeから、インスタンスメソッド(プロパティ)を継承している
  console.log(obj.__proto__.valueOf); //[Function: toString]
  console.log(obj.__proto__.hasOwnProperty); //[Function: hasOwnProperty]

  //obj自身には、プロパティは存在していません
  // console.log(obj.prototype.valueOf); //TypeError: Cannot read property 'valueOf' of undefined
  // console.log(obj.prototype.hasOwnProperty); //TypeError: Cannot read property 'hasOwnProperty' of undefined

  //自身のプロパティに存在しない場合は、ひとつ上の階層のprototypeをルックアップするのが、プロトタイプチェーン
  console.log(obj.valueOf()); //{ name: 'Tom' }
  console.log(obj.hasOwnProperty('name')); //true

  //プロパティは、自身に定義できる
  obj.age = 40;

  //インスタンスメソッドと同じ名前のプロパティも定義できる
  obj.valueOf = function () { return '改造します' };

  //自身のプロパティに存在するので、プロトタイプチェーンは遡らない。
  console.log(obj.valueOf()); //'改造します'

  //for in文は、まず、自身のプロパティの中で、列挙可能なプロパティを取り出します。
  for (const key in obj) {
    console.log(key); //name age valueOf
  }

  //プロトタイプチェーン(Object.prototypeとイコール)に、独自のプロパティを定義する
  obj.__proto__.tsujike = function () { return '独自プロパティ1' };

  //objはプロトタイプチェーンを遡って、tsujikeを呼び出せる
  console.log(obj.tsujike()); //'独自プロパティ1'

  //for in文は、この独自プロパティも取り出してしまう(これを列挙させないよう定義するのがObject.defineProperty())
  for (const key in obj) {
    console.log(key); //name age valueOf tsujike
  }

  //もちろん、イコールなので、Object.prototypeに定義した、独自プロパティでも同じことが起きる
  Object.prototype.kenzo = function () { return '独自プロパティ2' };

  console.log(obj.kenzo()); //'独自プロパティ2'

  for (const key in obj) {
    console.log(key); //name age valueOf tsujike kenzo
  }

  //hasOwnProperty()は、あくまで、自身のプロパティをチェックしている
  console.log(obj.hasOwnProperty('valueOf')); //true
  console.log(obj.hasOwnProperty('kenzo')); //false

}

すべてのオブジェクトのプロトタイプチェーンの到着点はnullというのも、アドレナリンが出まくった腹落ちでした。

  //Arrayオブジェクトを生成する
  const arr = [];

  //全てのオブジェクトのprototypeチェーンの到着点はnull
  console.log(arr.__proto__.__proto__.__proto__); //null

わたしのブログでいうと、このあたりの記事です。

第2位 クロージャ

JavaScriptの関数は、すべてクロージャです。

そして、ES6から導入されたクラス構文も、JavaScriptがクラスベースの言語になったわけではなく、それまでのコードの書き方の、糖衣構文に過ぎません。

クロージャとは、関数内で使われる変数の参照を関数スコープ外に持つ関数です。

function myFunction2_11_07() {

  //クロージャによるクラス
  function VegetableA(vegetable) {
    this.getVegetable = function () {
      return vegetable;
    }
    this.makeSandwich = function (topping) {
      return vegetable + 'and' + topping;
    }
  }

  //prototypeによるクラス
  function VegetableB(vegetable) {
    this.vegetable = vegetable
  }

  VegetableB.prototype.getVegetable = function () {
    return this.vegetable;
  }

  VegetableB.prototype.makeSandwich = function (topping) {
    return this.vegetable + 'and' + topping;
  }

  //確認
  const a = new VegetableA('レタス');
  console.log(a.getVegetable()); //レタス
  console.log(a.makeSandwich('トマト')); //レタスandトマト

  const b = new VegetableB('レタス');
  console.log(b.getVegetable()); //レタス
  console.log(b.makeSandwich('チーズ')); //レタスandチーズ

}

ES6のクラス構文の登場により、クロージャを意識する必要が無くなりましたが、ゲッターセッターの理解が深まりました。

function myFunction2_11_06() {

  function makeSandwich() {

    let vegetable = undefined;
    const obj = {

      set(newVegetable) {
        vegetable = newVegetable;
      },

      get() {
        return vegetable;
      },

      type() {
        return typeof vegetable
      }

    };
    return obj;
  }

  const sw = makeSandwich();
  console.log(sw.type()); //undefined

  sw.set('レタス');
  console.log(sw.get()); //レタス
  console.log(sw.type()); //string

}

わたしのブログでいうと、このあたりです。

第1位 ES6最高

プログラミング言語の発展は、「コーディングを楽にしよう」という改善の繰り返しです。

最新の記法を守り、タイプミスさえ気をつければ、ユーザーは複雑な実装の中身を意識することなく、プログラミングできるのです。

JavaScriptは、ES6により、モダンなプログラミング言語にかなり近づきました。

古い構文新しい構文主な書き方
スコープの監視const,letlet x;
文字列の連結テンプレート文字列${name}:${age}
無名関数アロー関数() => {}
配列コンストラクタ配列リテラル[]
引数の条件判定デフォルト引数f(arg = ‘hoge’){}
argumentsレスト構文f(…arg){}
コンストラクタとprototypeメソッドクラス構文class Class{}
即時関数とカプセル化クラス構文class Class{}
ディクショナリMapオブジェクトnew Map()
列挙とイテレーター列挙可能オブジェクトfor of文など

EffectiveJavaScriptを読み解く際は、「ES6構文で書けば解決するんだけど、考察すべきかな・・・」という葛藤の連続でした。

なので、 「ES6で書けることがどれほどありがたいことなのか」 が第1位となりました。

まとめ

以上で、「ノンプロ研EffectiveJavaScript輪読会」のまとめをお届けしました。

そして、これまで考察したコードを、わたしのGitHubにまとめました。

まちがいもたくさんあると思いますが、ご容赦ください。

GitHub - tsujike/EffectiveJavaScript: 書籍EffectiveJavaScriptをGoogle Apps ScriptのV8で読み替えたコードです
書籍EffectiveJavaScriptをGoogle Apps ScriptのV8で読み替えたコードです - GitHub - tsujike/EffectiveJavaScript: 書籍EffectiveJavaScriptをGoogle Apps ScriptのV8で読み替えたコードです

この輪読会は、だんだん参加者が減っていくものだろう、というのが大方の予想でした。

しかしながら、みんなで支え合って、無事、完走できたことは最高のよろこびです。

ノンプロ研の仲間に、感謝申し上げます。

次の企画はもちろん・・・「EffectiveGoogleAppsScript」です!よいお年を!

タイトルとURLをコピーしました