Open3
GAS
- JSはimportやrequireで別ファイルのモジュール名を指定して読み込む必要があるがGASは不要
- 名前空間は存在しないが一昔前のJavaScript のような対策で名前空間を持たせるような書き方は可能
- Web ブラウザや Node.js などの環境で JavaScript を実行する場合、ノンブロッキングで基本的に処理(非同期)
- GAS では、ノンブロッキングではなくブロッキング処理が採用(同期処理)
- 定番なのは、 TypeScript + clasp + VS Code の構成
- ステップ数が多くなるとローカルでの開発はほぼ必須
警告ログ出力
console.warn(e.stack);
- use strict(厳格モード)
- やること
- ミスからエラーへの変換(従来は受け入れていた一部のミスをエラーに変更)
- オブジェクトのプロパティへの代入に失敗すると(例えば読み取り専用であるなど)、 TypeError が発生
- 削除不可能なプロパティを削除しようとすると TypeError が発生
- 偶発的にグローバル変数を作成できないようにする
- 未宣言の変数に代入すると ReferenceError が発生
- ミスからエラーへの変換(従来は受け入れていた一部のミスをエラーに変更)
- strictモード非準拠のコードも存在する。スクリプト全体にstrict宣言をするより各関数ごとに絞る方が良い
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode
- https://qiita.com/TakashiSasaki/items/cb23a605d9028ae2db34
- やること
- スコープの限定
- オブジェクト生成
- 名前空間(namespace)
- グローバル変数の生成を抑える
- 名前空間(namespace)
- 即時関数
- 関数の名前被りを防ぐ、グローバル変数の生成を抑えるメリット
- その場限りの処理を行いたい場合は即時関数で実行
- オブジェクト生成
- constの宣言後、オブジェクトのプロパティの追加・更新・削除は可能
- 関数の結果をキャッシュする
- オブジェクトのプロパティが予期せず変更されることを防ぐ:Object.freeze()
- 直属のプロパティのみが対象なのでその下は対象外の為追加・更新・削除可能
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
- 繰り返し処理
- 無限ループにならないよう注意(do..whileやwhileで起こりがち)
- キー/バリューを頻繁に追加や削除する場合はMapを使う
- 膨大な配列を検索する場合はキー/バリューに変換してから検索した方が速い
- https://qiita.com/nuko-suke/items/50ba4e35289e98d95753
- ループの識別にラベルを使える
- 複数繰り返し処理がネストされている場合にラベル設定していればbreakやcontinueで指定したラベルつき文が対象となる
- オブジェクトは順番が担保されない
- 順番を守りたい場合は整数値の名前でループする配列を使用する
- 配列とオブジェクトどちらに対して利用するのか?
- for...in:オブジェクトにあるすべての列挙可能なプロパティが対象(プロパティ名に対し反復処理)
- for...of:(プロパティの値に対し反復処理)
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Loops_and_iteration
- hasOwnProperty():指定されたプロパティを持っているかどうかを示す論理値を返す
- includes() : Array インスタンスのメソッド、特定の要素が配列に含まれているかを示す論理値を返す
-
[{},{},{}]
といった配列オブジェクトの存在チェック:issues.find(({ title })=>{return title === issueText})
- 分割代入(
{ title }
の部分)でオブジェクト(issues->issue)からプロパティ(issue.title)を取り出して別個の変数(title)に代入する
- 分割代入(
- Array.prototype.filter(): Array インスタンスのメソッドで、指定された配列の中から指定された関数で実装されているテストに合格した要素だけを抽出したシャローコピーを作成
- 指定した配列の要素を削除する用途で利用する
- JSDoc
-
https://ics.media/entry/6789/
- コードヒント/コード補完
- APIリファレンスの生成
-
https://github.com/izumisan/cheatsheet/blob/master/JSDoc.md
- アノテーション(JSDocタグ)を追加することで、APIドキュメントを自動生成することができる
-
https://ics.media/entry/6789/
- import
- ソースファイルの中で import 宣言を使用→ランタイムがそのファイルをモジュールと見なす必要有
- HTML では、type="module" を <script> タグに加えることがこれに相当
- モジュールは、自動的に厳格モードとして解釈
- manifest.json
- https://developers.google.com/apps-script/concepts/manifests?hl=ja
- ランタイムバージョン指定
- V8 or STABLE(Rhino) or DEPRECATED_ES5(Rhinoランタイム)が指定可能
- V8 ランタイムの機能
- 最新の ECMAScript 構文
- 関数検出の改善
- function*
- ジェネレータ関数
- コールバック地獄や制御の逆転などのようなコールバックの問題を軽減してくれる
-
https://qiita.com/hitsujiwool/items/316f3e8a41fb7dc3a119
- generatorを使って非同期処理を同期的に書ける
- yield:関数から一旦抜けることができる
- ジェネレータ関数呼び出しだけでは関数実行されない→nextメソッドを呼び出しで初めて実行
- yieldまで実行されて止まる
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function*
- ジェネレータ関数
typescript
- tsは型検査のみでコンパイルは別のツールが行います
- 複雑な静的型検査(型パズル)より動的な型検査のほうが望ましいこともあるので適材適所
- try-catchのcatch(e)のeはどんな値が入ってくるか分からないので型はanyが基本
- 定数の書き方
- as const satisfies { 型 }で定義
- https://zenn.dev/watabo_shi/articles/3e27a85e983ea9
- オプションパラメーター(?):引数が省略可能になる
Jest
-
inputとoutputを明確にする
-
beforeEach:テストを実行する前に、関数を実行
-
mockReturnValueOnce:モック関数を1回呼び出したときに返す値をセット
-
特定のテストケースだけ実行したい場合:onlyをdescribeやtestの後ろにつける
-
まとめ方
- describe:関連するテストをグループ化、どういうモジュールや機能なのか、テストする対象を記述するのがよいかも
- it、testはどちらでも意味としては同じ、ユースケースや振る舞いを表現
- https://blog.stenyan.jp/entry/2024/04/02/194614
-
toHaveBeenCalledWith:モック関数が特定の引数を与えられて呼び出されたことを確認する
疑問
- interfaceとtypeの違い、ユースケース
- 汎用性と処理の効率性は両立しづらい
- コールバック関数で汎用性と効率性を両立させる
- 委譲と役割が似ている
- 宣言と実行を分ける
- call()
- thisを指定できる→別のオブジェクトのメソッド利用できる
- 使用するthis側のプロパティを操作することはできない→書き換えようとするとエラーになる
- 可読性悪いので、必ず使うべきユースケースが思いつかない...
var myapp = {};
myapp.color = "green";
myapp.paint = function (str) {
console.log(str + ":" + this.color);
}
test = {
fn: function(arg) {
this.paint(arg)
}
};
test.fn.call(myapp, "aaa");
// aaa:green
// thisを指定できるので別のオブジェクトのメソッド・引数指定できる
// そのときだけ別のオブジェクトを継承できるイメージか
自己定義関数
- 初期化による準備作業がある→準備作業の実行を1度だけ行うときに便利
- 自分自身の実装を更新させる(自己定義関数)
- 関数は参照渡しなので、別の変数に代入した場合は古い定義を呼び出し続ける形になる為意図した通りに動かない
let scareMe = function () {
console.log("1回目")
let i = 2;
scareMe = function () {
console.log(i + "回目")
i++;
}
}
scareMe();
scareMe();
scareMe();
即時関数
- オブジェクトプロパティを定義するときにも使える
- グローバル変数を残さずにしたい作業を包む
- ブックマークレットでも頻出(グローバル名前空間を汚染しない為)
- 即時オブジェクト初期化
- initメソッドのあるオブジェクトを利用
- オブジェクト作成後にこのメソッドを実行
- メリット:初期化手続き全体に構造を作ることができる
- デメリット:ミニファイで効率的に処理してくれない
初期化時分岐
- プログラム動作中全く変更されない条件がある場合、条件の判断を一度だけ初期化時行う
- 初期化時判断しオブジェクトメソッドに追加する
関数プロパティによるメモ化パターン
- 関数の結果をキャッシュ(メモ化)するのはカスタムプロパティの用途の一つ
- 関数が次回呼ばれるとき重い処理をさせずに済む
- 関数に渡されたパラメータ(引数)をキーに計算結果を値にする
- パラメータを増やして複雑にするときはJSONデータにシリアライズしキーとする
カリー化
- 関数に部分適用を理解させ処理させること
- 一部の引数を関数に渡したとき、クロージャに残りの引数を渡すことで動作する関数(クロージャ)を返す
ファクトリ
- オブジェクトをケースごとに作成する
- 親の処理を継承→ケースごとのプロパティを設定→オブジェクト生成時に対象ケースのプロパティをセット
- ユースケース:共通処理+ケースごとに差分が少量発生する場合に使えるか
function CarMaker(){}
CarMaker.prototype.drive = function() {
return "Vroom, I have " + this.doors + " doors";
};
CarMaker.factory = function (type) {
var constr = type,
newcar;
// メソッドの存在チェック
if (typeof CarMaker[constr] !== "function") {
throw {
name: "Error",
message: constr + " doesn't exist"
}
}
// 親オブジェクトを元にインスタンス生成しprototypeにセット
if (typeof CarMaker[constr].prototype.drive !== "function") {
CarMaker[constr].prototype = new CarMaker();
}
newcar = new CarMaker[constr](); // プロパティをセットするメソッドの実行
return newcar;
}
CarMaker.Compact = function() {
this.doors = 4;
}
CarMaker.Convertible = function() {
this.doors = 2;
}
CarMaker.SUV = function() {
this.doors = 17;
}
var collora = CarMaker.factory("SUV")
console.log(collora.drive());
イテレータ
- 一度に全データをロードせず、必要な時に次の要素を取り出す「遅延評価」ができる
- メモリを節約しながらデータ処理が可能、大量データを扱うときに便利
- 特定のロジックでカスタムな反復処理を実装できる
- イテレータのオーバーヘッドが生じるため、特に単純な繰り返し処理には不向き
var agg = (function(){
var index = 0,
data = [1,2,3,4,5,6,7],
length = data.length;
return {
next: function() {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},
hasNext: function () {
return index < length;
}
}
}())
while (agg.hasNext()) {
console.log(agg.next())
}