どうも。つじけ(tsujikenzo)です。もう、明日でクリスマスなんですね。今回は「ノンプロ研EffectiveJavaScript輪読会 #最終回」についてお届けします。
本記事は、わたしの所属しているコミュニティ、ノンプログラマーのためのスキルアップ研究会の『ノンプロ研 Advent Calendar 2021』 24日目の記事です。ノンプロ研では「働く力」を上げるというテーマを元に、様々な学びを得る機会を提供頂いております。私も少しでもお役に立てればと思って活動を続けております。
ElementsJavaScript輪読会とは
2021年夏に、ノンプロ研の有志が集まり、こちらの書籍を読み解いていく(いわゆる輪読会)が企画されました。
集まったメンバーは、主にGAS中級講座卒業生や受講生で、10名弱の参加となりました。
輪読会の進め方
輪読会の進行は、このような感じです。
- ひとり1~2項目についてプレゼン(スライド有無など自由)
- 項目あたり10~15分まで ※項目による
- プレゼン後に時間があれば意見交換
だいたい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を使っているのかを意識しよう | 橘乃吏子 |
項目2 | JavaScriptの浮動小数点数を理解しよう | つじけ |
項目3 | 暗黙の型変換に注意しよう | タカハシノリアキ |
項目4 | オブジェクトラッパーよりもプリミティブが好ましい | etau |
項目5 | 型が異なるときに ==を使わない | こはた |
項目6 | セミコロン挿入の限度を学ぼう | Yukihiro Yamaguchi |
項目7 | 文字列は16ビットの符号単位を並べたシーケンスとして考えよう | SWD |
第2回 2021年09月08日
項目 | 内容 | 担当 |
---|---|---|
項目8 | グローバルオブジェクトを使うのは、最小限にとどめる | こはた |
項目9 | ローカル変数は、かならず宣言しよう | タカハシノリアキ |
項目10 | withは避けよう | スキップ |
項目11 | クロージャと仲良くしよう | つじけ |
項目12 | 変数の巻き上げ(ホイスティング)を理解する | Yukihiro Yamaguchi |
項目13 | ローカルスコープを作るには即時関数式(IIFE)を使おう | SWD |
項目14 | 名前付き関数式のスコープは可搬性がないので注意しよう | etau |
項目15 | ブロックローカルな関数宣言のスコープも可搬性がないので注意しよう | タカハシノリアキ |
項目16 | evalでローカル変数を作らない | スキップ |
項目17 | 直接evalより、間接evalが好ましい | スキップ |
第3回 2021年09月26日
項目 | 内容 | 担当 |
---|---|---|
項目18 | 関数、メソッド、コンストラクタの、呼び出しの違いを理解する | なー |
項目19 | 高階関数を快適に使えるようになろう | Masayoshi Kataoka |
項目20 | カスタムレシーバ付きでメソッドを呼びだすにはcallを使う | こはた |
項目21 | いくつでも引数をとれる関数を呼び出すにはapplyを使おう | SWD |
項目22 | 可変長引数関数を作るには、argumentsを使う | つじけ |
項目23 | argumentsオブジェクトを書き換えない | etau |
項目24 | argumentsへのリファレンスは変数に保存する | タカハシノリアキ |
第4回 2021年10月14日
項目 | 内容 | 担当 |
---|---|---|
項目25 | 固定レシーバを持つメソッドを抽出するにはbindを使う | SWD |
項目26 | 関数をカリー化するには、bindを使う | etau |
項目27 | コードをカプセル化するには、文字列ではなくクロージャを使う | こはた |
項目28 | 関数のtoStringメソッドに依存するのは避けよう | つじけ |
項目29 | 非標準のスタック調査プロパティを使うのは避けよう | Masayoshi Kataoka |
項目30 | prototype、getPrototypeOf、__proto__の違いを理解する | Yukihiro Yamaguchi |
項目31 | __proto__よりもObject.getPrototypeOfが好ましい | タカハシノリアキ |
項目32 | __proto__は決して変更しないこと | タカハシノリアキ |
第5回 2021年10月31日
項目 | 内容 | 担当 |
---|---|---|
項目33 | newに依存しないコンストラクタの作り方 | etau |
項目34 | メソッドをプロトタイプに格納しよう | タカハシノリアキ |
項目35 | プライベートデータの格納にはクロージャを使おう | つじけ |
項目36 | インスタンスの状態は、インスタンスオブジェクトにだけ保存する | SWD |
項目37 | thisの暗黙的な結合を理解しよう | こはた |
項目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 | 順序を持つコレクションには、ディクショナリではなく配列を使おう | タカハシノリアキ |
項目47 | Object.prototypeには、列挙されるプロパティを決して追加しない | つじけ |
第7回 2021年11月28日
項目 | 内容 | 担当 |
---|---|---|
項目48 | 列挙の実行中にオブジェクトを変更しない | etau |
項目49 | 配列の反復処理には、for…inループではなく、forループを使おう | なー |
項目50 | 複数のループよりも反復メソッドが好ましい | Yukihiro Yamaguchi |
項目51 | 「配列のようなオブジェクト」にも、総称的な配列メソッドを再利用できる | こはた |
項目52 | 配列コンストラクタよりも配列リテラルのほうが好ましい | つじけ |
第8回 2021年12月21日
項目 | 内容 | 担当 |
---|---|---|
項目53 | 一貫した規約を維持しよう | SWD |
項目54 | undefinedは「値なし」として扱う | なー |
項目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,let | let 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にまとめました。
まちがいもたくさんあると思いますが、ご容赦ください。
この輪読会は、だんだん参加者が減っていくものだろう、というのが大方の予想でした。
しかしながら、みんなで支え合って、無事、完走できたことは最高のよろこびです。
ノンプロ研の仲間に、感謝申し上げます。
次の企画はもちろん・・・「EffectiveGoogleAppsScript」です!よいお年を!