初めて JavaScript(ES5) を業務で使ったので、備忘録がてらまとめてみました。
Web 屋の皆さんからすればポンコツだと思いますが、非 Web 屋で生きる Javasript 知らないマンの一視点として参考になれば(?)と思います。間違いやオススメなどもありましたらぜひご教授ください
最初に結論
今回の開発で得た知見やら結論やら所感やらを、端的にまとめておきます。
一行で:
- Q: なぜ ES5?
- A1: JavaScript 全然知らないし、規模も 2-3KL だから、まずは ES5 から押さえるべきだと思った
三行で:
- 規模は 2-3KL だったけど ES5 直書きキツイ、もうやりたくない
- 臆せず最初からフレームワークやライブラリにもっと頼るべきだった(後になればなるほど移行コストがでかくて詰む)
- 調べ物してたら技術的好奇心で時間食われるので自制が必要
Q&A形式でもう少し詳しく:
- Q: なぜ ES5 を使った?
- A1: JavaScript について全くの無知だったので、まず ES5 に触れて勉強すべきだと思ったから
- A2: (「なんかムズそう」と新しい技術を敬遠してしまうチキン精神)
- Q: ES5 を使ってみてどうだった?
- A1: 言語仕様が不便すぎて苦しかった
- A2: 巷にフレームワークが溢れている理由がよくわかった
- A3: 2-3KL 程度なら何とか実装できるが、これ以上の規模になると正直苦しい
- A4: サブクラス使えない縛り、import使えない縛りが特に辛い
- Q: 今回 JavaScript に触れて良かったことは?
- A1: ES5 について知れた
- A2: VSCode の使い方を学べた
- A3: mocha や chai の使い方を学べた
- A4: JavaScript という面白い言語仕様に出逢えた(考え方や暇つぶしの幅が広がった)
- Q: 次 JavaScript 使うとしたらどうする?
- とりあえず ES5 直書きは無い
- 最低でも ES6 直書きか、(IEを想定するなら) Babel あたりが良いのかしら?
- requireJS 等でモジュールインポートを実現したい
- 今回画面を全くいじってないが、次使うとしたら……
- Vue.js やら何やらフレームワークは臆せず使っていきたい
- CSS はちゃんと勉強しないとなぁ(ボックスモデルとかよくわかってない)
- 今回バックエンドを全くいじってないが、次使うとしたら……
- やること多くて大変そう
- フルスタックみたく一人で全部やるのは(現時点の実力では)厳しそう
- チームで分業ならぜひ挑戦してみたい
- やること多くて大変そう
開発コンテキスト
開発対象など前提条件をまとめておきます。
今回作ることになったもの
多数の入力値から、なんか複雑な計算をして、色々と結果を表示する Web アプリ。
- 規模は 2-3KL
- サポートブラウザは IE 必須、それ以外は任意
- いわゆる社内システム開発
この時は IE の恐ろしさなど知る由もありませんでした
今回作ることになったもの(MVC観点)
MVC観点でまとめてみます。
- Model
- 「複雑な計算」がメイン
- ≒ 何十ものパラメータや計算式を用いた四則演算
- 1MB に満たない程度の CSV データに依存
- 「複雑な計算」がメイン
- View
- Bootstrap ベースで既にモックが組んである
- このモックは画面デザイン含めて先方と合意済み
- Controller
- 特に制約は無い
要するに計算部分(CSVデータの取り扱い含む)をいかに JavaScript で実現するかということと、それを View といかにして連携させるかというところが肝です。
View がモックとして既に定まっており、CSS や UI/UX について苦戦しなくても良いのは幸いでした。
今回作ることになったもの(その他制約)
- 開発マシンは Windows
- 社内のデフォ
- HTML/CSS/JavaScript を使うこと
- バックエンドは無し
- 公開先はイントラ内のみ
バックエンドが無く、またイントラ内ということで一般的な Web アプリにかかる種々の作業(連携、公開、セキュリティ全般)が無いのが幸いでした。
開発体制
- 開発は私一人
- 期間は二ヶ月
- ステークホルダー
私 -- 上司 x1 -- お客さん xN
- 打ち合わせは 1-2week に 1 回
- 遵守すべきプロセスは特に無し
幸か不幸か、小さな社内案件で制約が緩かったです。これが大きな案件だったり、社外向け製品だったりするとステークホルダーとプロセスが一気に増えます。
周辺の人脈
- システム屋なので Web に詳しい人がいない
- 業務中に Twitter やら何やらで部外者と喋るのは禁止
- Stackoverflow などへの投稿も禁止
頼れる人がいないという状態です。
私の実力
- HTML/CSS/JavaScript はほぼ素人
- テキストを行ソートするやつ とかインクリメンタルサーチな簡易ブックマークツール みたいなのを作る程度
- 趣味では主に Python で自己満ツールやら省力化やら
言語や開発手法など
言語や開発手法として何を選んだかについてまとめます。
(フォーマット)
- 検討結果: (何を使うことにしたか)
- 選択理由: (なぜそれを選んだか)
- 良かった: (実際に開発してみて良かった点)
- 悪かった: (実際に開発してみて悪かった点
(ここに思考過程やら事情やらをつらつらと)
JavaScript
- 検討結果: ES5 を使う
- 選択理由: そもそも JavaScript を理解してないため、まずは ES5 を直に触って勉強も兼ねるべきだと思ったから
- 良かった: ES5 の勉強になった
- 悪かった: 言語仕様が不便すぎて辛い
調べてみると JavaScript の書き方は色々あるようです。以下4つがあると思っています。
- (案1) ES5(古い JavaScript)を直接書く
- (案2) ES6(新しい JavaScript)を直接書く
- (案3) トランスコンパイラ(TypeScript など)で書く
- (案4) Babel で書く
自分は Python マンで、Python レベルの(というか一般的な LL レベルの)言語仕様は当たり前だと思っているので、できれば色々不便な ES5 は使いたくなかった。最低でも案2以降を選びたかったのですが、
- ES6 → IEが対応していない
- TypeScript や Babel など → ES5すら知らないマンがいきなり使ってもハマりそう
というわけで ES5 に逃げました。
おかげで勉強にはなりましたが、色々と苦しめられることにもなりました(詳しくは後述します)。これほどにフレームワーク類が溢れている理由を思い知った気がします。
IDE/エディタ
- 検討結果: VSCode を使う
- 選択理由: 消去法で残ったから
- 良かった: 全体的に
- 悪かった: 強いて言えばちょっと重い
消去法で VSCode になりました。以下は他選択肢と没理由です。
- 秀丸エディタ …… 普段愛用してますが本格的なコーディングには向いてないので不採用に。JS は動的型付けなこともあって IDE の力に頼らないと死ぬ。
- Eclipse …… 重い。
- Atom …… パッケージ管理が動かない(Windowsだと修羅の道らしい)。
- Vim …… 嫌い。マッスルメモリゲーは苦手。
- Sublime Text …… エディタ設定やプラグイン拡張やらがよくわからん。
VSCode の特徴:
- 今風のIDE(Qiitaでもよく登場する)
- サイドバーにはファイルツリーやら Git 操作やらデバッガやらがあって便利(邪魔なときには折り畳める)
- Microsoft 謹製だけあって無難な品質
- VSCode が登場した頃に遊んでみた時はそうでもなかった気がするけど……
実際に使ってみた感じでは、デフォ設定のままでも使いやすいですし、拡張機能でガシガシいじれますし、設定は json でちんぷんかんぷんですが調べればすぐ出てきますし、左ペインが地味に便利だし、と大満足です 。もう当面はこれでいいかなと思いました。
ただ、結構重たいのでちょろっといじる程度で立ち上げるのには向かないですかね。
ユニットテスト
- 検討結果: mocha + chai を使う
- 選択理由: 事例豊富、ドキュメント充実で無難そうだったから
- 良かった: 普段使ってるユニットテストと同じ要領でガシガシ書けた
- 悪かった: 特になし
私はユニットテストで足元を固めながら進めるマンであり、Python の unittest や C/C++ の Google Test みたいにコマンドベースで行えるようなブツを所望してました。色んなフレームワークがありますが、今回は(事例豊富で無難そうだったので) mocha + chai を選んでみました。
mocha + chai の特徴:
- インストールに node.js と npm が必要
- RSpec みたいな書き方で書く
- mocha と chai
- mocha → テストフレームワーク
- chai → アサーションライブラリ(mochaから呼び出す)
- chai は Assertionとか豊富 で使いやすそう
- ブラウザから実行することもできる
- 特に今回は IE 必須なので IE でもテストしたい
- 導入は mochaとchaiの最も基本的な使い方 - 30歳からのプログラミング を参考に
テストコードは JUnit スタイルしか知らない私ですが、普段通りサクサク書けたので快適でした。BDD な書き方をサポートしているだけであって、表示結果に日本語が使えるのも Good でした(下手な英語で書くよりも何百倍もわかりやすい)。
リアルタイムプレビュー
- 検討結果: livereloadx + livereload(Firefox拡張)
- 選択理由: 普段が Firefox 使いだから
- 良かった: Firefox上でリアルタイムプレビューを実現できた
- 悪かった: Firefox が新しいとたぶん動かない
修正する度にブラウザ上で F5 押して読み込んで……ってのは非常にしんどいです。自動で反映してくれないか、と思って調べたところ、
livereload で出来そうでした。やり方としてはこんな感じです。
- (1)動的プレビューしたい HTML ファイルのあるディレクトリ上で livereloadx を実行する
- 更新監視を行う役
- (2)Firefox で当該 HTML ファイルを開き、livereload を有効にする
- 監視結果を受け取ってブラウザをリロードする役
(2) については (1) の 作者ブログ 曰く、他にも色んな方法があるようですが、今回は一番楽そうな Firefox 拡張に頼りました(古いので Firefox Quantum だと動かないかも )。
デバッグ
- 検討結果: ユニットテストでは VSCode、ブラウザレベルでは Firefox F12 ツール
- 選択理由: 自然とこうなった(他に選択肢は無いように思える)
- 良かった: ブレークポイントとウォッチ式のおかげでだいぶ楽できた
- 悪かった: 特になし
長らく Firefox の Firebug を利用していたのですが、だいぶ前に使えなくなりましたよね。それからデバッグ手段には不安を抱いていたのですが、デフォの F12 でも十分役立ってくれました(Firebug に見慣れてるので慣れるのに時間かかりましたが)。
あと VSCode 上でのデバッグも何気に便利、というか超便利でした。console.log を使わなくてもブレークポイントで止めて周辺の変数を見ればいいですし、何よりウォッチ式が便利。今時の IDE って凄いんだなと改めて感心してました。
ビルド
- 検討結果: バッチファイル で頑張った
- 選択理由: ただのフォルダ作成とファイルコピーで済むため
- 良かった: (バッチファイルは書き慣れてるので)学習コストが少ない
- 悪かった: とはいうものの xcopy コマンドの仕様にちょっと苦戦
インタプリタの JavaScript にビルドなんてあんのか、と思ってたんですが、考えてみればリリース用物件を生成する際に(開発ソース一式をそのまま使うわけにはいかないので)あれこれやりますよね。簡単に調べてみると、普通にジャンルとして確立されていて、gulp.js あたりのワードはよく見ました。
が、今回は圧縮や難読化などもやらないですし、リリース物件に必要なファイルをコピーして準備するだけで済んだので、バッチファイルで手早く済ませました。
(JavaScript の話題関係無いので端折りますが)コピーには xcopy コマンドを使ったのですが、コピーの挙動が煩雑で少しハマっちゃいました。
規模測定(LOC)
- 検討結果: cloc を使用
- 選択理由: シンプル、軽い、コマンドベースで整形しやすいから
- 良かった: 期待どおりの働き
- 悪かった: 特になし
規模データなどの提出は求められてませんでしたが、個人的に生産性を知りたかったので、毎日ポチポチと計測するために導入してみました。
cloc は js ファイルもバッチリ対応しているようで、期待どおりの働きをしてくれて良い感じです。昔は VectorのGUIベースのカウンタ を使ってましたが、もう戻れませんね。
JavaScript の書き方
JavaScript にも色んな書き方がありますので、どんな書き方を採用するべきかが重要だと思いました。先にある程度学習した上で、決めていきました。
クラス定義
- 検討結果: やさしいクラスの作り方 に従う
- 選択理由: 読みやすいから
- 良かった: 読みやすい
- 悪かった: サブクラスが使えない、VSCodeで補完されない
そもそも ES5 はクラスをサポートしていませんが、クラスっぽい仕組み(プロトタイプベースを上手く活用してオブジェクト指向っぽく見せる)は可能なようです。
色々調べたところ 【javascript】やさしいクラスの作り方 - Qiita が一番見やすかったので採用しました。
定義:
// ※ はクラス名
※ = (function(){
var ※ = function(ARGS){
}
var p = ※.prototype;
p.METHODNAME = function(ARGS){
}
return ※;
})();
使用:
var obj1 = new ※(ARGS);
ただ、この書き方だからなのか VSCode がプロパティを補完してくれなかった のがキツかったですね。ワーキングメモリ弱いマンの私は何度も何度も VSCode 上を行ったり来たりして確認するハメになりました。
あと(後述しますが) サブクラスを実現できなかった のも痛かった。サブクラス使えない制約。何という縛りプレイ……
名前空間
- 検討結果:
var NAMESPACE_HOGE = NAMESPACE_HOGE || {};
を使う - 選択理由: やはりグローバル空間は汚染したくないので
- 良かった: グローバル空間の汚染を考えなくて済んだ
- 悪かった: 参照時の指定が長くなる(仕方ないが)
グローバル空間を汚染しないためにも名前空間は必須とのことなので、採用してみました。
util.js:
var MOD_UTIL = MOD_UTIL || {};
MOD_UTIL.is_undefined = function(obj){
return obj === void 0;
}
MOD_UTIL.※ = (function(){
var ※ = function(ARGS){
}
var p = ※.prototype;
p.METHODNAME = function(ARGS){
}
return ※;
})();
Python の import みたいな挙動で、自然に各モジュールにアクセスできる使い方が出来たので好感触でした。
ちなみに好き勝手な名前を使うと(主にデバッグ時に)どの変数が名前空間なのかわからなかったので、先頭の Prefix を統一( MOD_XXXX
)することで対処しました。
モジュールインポート
- 検討結果: chai の require を利用&本番実行時に ReferenceError 食らわないよう場合分け
- 選択理由: モジュール管理は学習コスト高そう&まずは素直にscriptベースの読み込みに慣れるべきだと思ったから
- 良かった: 勉強になった
- 悪かった: 場合分けコードが汚い、「A.js import B.js」が出来ない縛りプレイ
Python でいう import に相当する機能が当然存在すると思っていましたが、JavaScript の ES5 には無いんですね。
じゃあどうするのかというと、index.html などから script タグで js ファイルを読み込む(読み込んだ js ファイル内のオブジェクトは全部 グローバル空間に入る)しかない。つまり A.js から B.js を見たい場合、A.js では「グローバル空間に入ってるはずの、B.js のオブジェクト」にアクセスするようなコードを書く。当然、A.js 単体ではテストできないので、テストする時は test.html みたいなの作って、script タグで B.js, A.js の順に読み込ませてやる必要がある……という考え方だと思ってます。合ってます?
でも mocha + chai だと普通に require 文が使えてるんですよね。「じゃあこれでいいじゃん」と安易に判断してしまった私。 テストコードから離れた途端、ReferenceError を食らってしまいます。require 文は chai が内蔵している機能なので当然そうなりますよね。
……じゃあどうすんのって話で、製品コードでも chai を読み込もうかと思ったのですがテスト用ライブラリを読み込むのもどうかと思い、やめました。最終的には 泥臭く場合分けしちゃいました。C 言語でいう ifdef みたいなやつです。
コードで示すと、fuga.js を import してる hoge.js はこんな感じです。
var NAMESPACE_HOGE = NAMESPACE_HOGE || {};
// import from hoge.js to fuga.js
// ★めちゃ煩雑。毎回これを書くのは正直しんどい
var global = (function(){return this})();
if(!('chai' in global)){
var mod_fuga = require('./fuga.js').namespace_fuga;
// index.html から実行時に require が undefined になるため
// script タグからロードされてる名前空間を直接見に行く.
if(mod_fuga === void 0){
mod_fuga = NAMESPACE_FUGA;
}
}else{
mod_fuga = NAMESPACE_FUGA;
};
// wrong ) mod_fuga.FugaClass ← index.html から実行時に見えない(なぜ?)
// correct) NAMESPACE_HOGE.mod_fuga.FugaClass
NAMESPACE_HOGE.mod_fuga = mod_fuga;
// 本体
NAMESPACE_HOGE.func = function(obj){...}
NAMESPACE_HOGE.HogeClass = (function(){
var HogeClass = function(){...}
...
return HogeClass;
})();
// 本体おわり
// ブラウザから実行するとエラーになるのを回避
var global = (function(){return this})();
if(!('chai' in global)){
exports.namespace_hoge = NAMESPACE_HOGE;
};
どうしてこうなった 実行経路が以下三パターンあるからですね。
- (1) コンソールから実行するパターン
- 例:
mocha hoge.js
- 例:
- (2) ブラウザ版mocha から実行するパターン
- (3) index.html(製品のエントリポイント) から実行するパターン
この三パターン全ての場合分けを愚直に書いたわけですが……これは失敗でした。import の度に上記を書くのは心が折れる(折れた)ので import無しという縛りプレイ に。辛い。本当に失敗です。
もっと良い方法は無いんでしょうかね……。requireJS あたりを使うのがベターっぽい……と気付いたのが開発の佳境前。が、全部を AMD 表記で書きなおす+αの手間暇を越えられる自信がなかったので、今回の採用は見送りました。最初からやっとくんだった……。
サブクラス(継承)
- 検討結果: 使用しない
- 選択理由: JavaScriptの理解が及ばなくて実現できなかった
- 良かった: 特になし
- 悪かった: サブクラス使えない縛りキツイ
上記の「クラス定義」を使う前提で、サブクラスを実現しようとしたのですが、prototype やら constructor やら何やらを上手いこといじらねばならず、結局どうすればいいかわからずに詰みました。昔遊びで買ったサイ本(6版)を読んで勉強してみたりもしたのですが。
しかしそうなるとデザインパターンも使えないわけで、地味にキツかったです。Mix-in もどきやら Duck Typing やら動的プログラミングの恩恵を活用してなんとか凌いだという感じです。
コーディングルールやフォーマット
- 検討結果: 明示的な規則や仕組みは無し
- 選択理由: 一人開発だから&この辺を整備する手間が惜しかったから
- 良かった: 特になし
- 悪かった: 引継ぎ用に「こんなルールで書いてますよ」的な暗黙知をドキュメントしたが 正直面倒くさかった
特に意識せずオレオレで進めてました。
が、終盤になって「引継ぎ先の人にわかりづらいよなぁ」と気付き、暗黙知を文章として残すことにしました。……が、ここが地味に手間取った。最初からフォーマッタなり何なり利用してれば楽できたのに。反省です。
そもそもオレオレで書いたものはえてして不完全なので、 JavaScriptのスタイルガイドまとめ(おすすめ4選) - Qiita などを読んで既存の叡智にあやかるべきなんですよね。申し訳ない。
CSV データの取扱い
今回の開発は計算処理がメインなのですが、その過程で CSV データを使います。そのため Javacsript から扱えるように変換する必要があります。
- 検討結果: Python で CSV to data.js 変換するスクリを書いた(中身は object)
- 選択理由: Python に慣れてるから。データ規模小さいのでバックエンド要らないから
- 良かった: 支障無く変換できた
- 悪かった: 特になし
まず JavaScript から CSV データを簡単に読む方法はないので変換は必須です。最初は DB に持たせるという話だったのですが、
- データ規模が 1MB 未満と小さかった
- 別に見られても困るデータではない
というわけでクライアント側で持たせることにしました。
変換は Python でゴリゴリ書きました。 jsonライブラリはマジで便利 ですね。
出力フォーマットについては、最初は data.json みたいな json フォーマットにしていたのですが、JavaScript から json ファイルを読むのは辛い(動的な通信が必要だしブラウザ側のセキュリティで動作に引っかかる)感じだったので、data.js の中に data = {データの中身};
みたいな感じで書いて、これを index.html から script タグでロードするというやり方にしました。こういう時って一般的にどうやるんですかね。
ハマったところ
以降では今回の開発でハマった点(上記で書いたものは除く)を雑多に書きたいと思います。
mocha + chai のインストール
- 一言で: npm でインストールするだけだが プロキシとリポジトリ設定でハマった
mocha や chai のインストールには npm(もっというと node.js)が必要です。が、ここでちょっとハマりました。
結論を書くと、以下のような手順が必要でした。
set HTTP_PROXY=http://(会社のプロキシサーバ):(ポート)
set HTTPS_PROXY=https://(会社のプロキシサーバ):(ポート)
call npm -g config set proxy %HTTP_PROXY%
call npm -g config set https-proxy %HTTPS_PROXY%
call npm -g config set registry http://registry.npmjs.org/
npm install -g mocha
cd <ProjectPath>
npm install chai
要するにプロキシと set registory
の二点。
知らぬ間に ES6 構文使ってる&IE が ES6 に対応してない
- 一言で: 知らない間に ES6 構文を使っていて、かつ IE が ES6 に対応してないせいで IE でのみ動かない 現象に遭遇、何度かハマった
「JavaScript で XXXX がやりたいんだけどどうすれば?」を調べる際、ググっては、ヒットした Qiita を読んでいたのですが、大半は ES6+ を前提に書かれています。当然サポートしてない IE では動きません。「なんで IE でだけ動かない?」とハマりました。
一例:
- デフォルト引数
-
for-of
- python でいう for in ループ的なやつ
-
Spread syntax
- python でいう
ls=[a,b,c]; func(**ls);
にあたる表記
- python でいう
- Number.isInteger()
MDN Web Docs が各ブラウザのサポート状況をまとめているのがありがたいですね。しまいには「javascript XXXX MDN」でググって、まず IE のサポート有無をチェックするまでになりました
未定義オブジェクトが undefined のまま進行する件
- 一言で: 未定義のオブジェクトが undefined のまま処理が進む ため、問題が起きた時に言及究明しづらい
return を retrun と書き間違えて 10 分悩むマンであり、Typo 多いマンでもある私には地味に地獄でした。エラーメッセージやデバッガを読んでも中々気付けません。静的型付けを導入するトランスコンパイラが生まれるのもわかる気がします。
1 + '1' = 11
- 一言で: 1+1 が 2 じゃなくて 11 になる 現象が起きてハマった
数値計算を行うつもりが、一部変数の値(特にデータから読み込んできた値など)が string になっていたせいで、計算が 1+'1'=11
みたいに文字列連結になってしまう。この時、連結後の値が数値変換されて数値として表出する。
これでハマりました。 11
は数字なので、パっと見、計算ミスかと思っちゃいます。
今回の例で言えば、CSV を data.js に変換した時に「値を全部 string で持たせていた」のが諸悪の根源だったので、数値データ部分は全部数値に変換するようにしました。「呼び出し元を書く時に適切に変換できるさ」と思っていた当時の自分を殴りたい。
0 === '0' は false
- 一言で:
v === 0
← v が 0 なのに false になるのなんで?に遭遇してハマった
これも上記の 11 問題と同様ですが、
// データ data が hoge を搭載していない場合
if(data.hoge_count === 0){
...
}else{
// ここで XXX / data.hoge_count ← こんな数式を使っている //
}
こんなコードを書いていてゼロ除算が発生してハマりました。
原因は data.hoge_count
の値が string だったため '0'===0
となってしまい、false になるというものです。
変数の型はラッパーオブジェクトとか作ってちゃんとガードしてあげないとダメですね。 プログラマの意識や気付きを信用してはいけない。尤も優秀な人ならすぐ気付けるのでしょうが、retrun マンの私は違います。この辺のつまらないミスで時間を潰さないよう、きっちり仕組みを作り込むべきだってことを痛感しました。
(余談)ついでに言えば今回 lint を導入し忘れていたので ESLint あたりでも最初から導入するべきでしたね(これも気付いたのが佳境前で、試しに適用したら真っ赤に染まって修正コストがえらかったので断念しまいた)。ESLint 使えば、こういうつまらないミスもたぶん潰せるんですよね?
浮動小数点絡みでテストが通らない
- 一言で: コードでたとえると
assert.strictEqual(100, 99.999999999)
みたいな感じでアサーションにしくじる
今回の開発は計算処理がメインのため、浮動小数点が普通に登場するわけですが、ユニットテストでアサーションする時になぜか結果が通らずハマりました。 浮動小数点の小さな誤差で strict equal にしくじってました。
対処法として、適当なところで丸めることにしました。
var diff = Math.abs(actual_value - expect_value);
var precision = 1000000;
assert.isAtMost(diff*precision, 1);
幸いにも、本製品は計算結果に小数点第n位レベルの厳密性を要求しなかったので、これで凌げました。
動的に更新される要素に対するイベントハンドラ
- 一言で: クリック時のイベントハンドラを登録してるのに起動しない現象でハマった
jQuery の話です。 $(selector).click(...)
でクリック時のイベントハンドラを登録したとします。この時、イベントハンドラに登録されるのは「その時 selector セレクタにより見えていた要素」だけなんですよね。
何が言いたいかというと 後から動的に追加された要素は(たとえ selector セレクタに該当したとしても)ハンドリングされない。……言われてみれば当たり前ですが、中々気付けずガッツリハマってしまいました。偶然ひらめかなかったらもっと悩んでたでしょう。
こういう時に地力(技術に関する基礎知識)が求められますよね。一度体系的に勉強しておくべきだなと改めて思いました。
(余談)もっと賢いやり方として live()を使う とか バブリングを利用して親要素側でハンドリングする とかあるみたいですね。live() については jQuery バージョン次第では on() に統一されていたり、とややこしいみたいで。
this の指す先が変化する件
- 一言で: this の指す先って変化するんすね……
たとえば、なんちゃってクラスの中で jQuery のイベントハンドラを定義している場合に、イベントハンドラの中から this (このインスタンス) を見る場合。
私は普通に this.property1
と書いていたのですが、これだと動きませんでした。
- (原因) this の中身が jQuery オブジェクトを指しているから
そういえば「this が変化する」的な文言はチラチラ見覚えがありました。こういうことだったのか……
- (対策) 外側のスコープで
var self = this;
とエイリアスを作っておき、アクセスする時はself.property1
のようにする
他にも this はまだまだハマリどころが多そうなので、これもしっかり勉強しておくべきなんでしょうね。あ、ES6 では確かこの呪いから逃げられるんでしたっけ。
いつの間にか時間が過ぎてる……!?
- 一言で: JavaScript について調べ事 → いつの間にかネットサーフィンの罠
「ハマった」とは少し違いますが取り上げておきます。
JavaScript の言語仕様は卑怯だと思います。「俺だったらどう料理してやろうか」とあれこれ考えたくなっちゃうような誘惑を持ってますよね。サイ本もボリューミーですし、Qiita にもネタ記事含めてたくさんありふれてますし。
調べ事をしているつもりが、いつの間にかネタ記事に読みふけっている という事態に…… これは気をつけないといけません。
頼れる人がいない
- 一言で: 一応完遂はできたけど 遠回り感や非効率感 がすごい……
これも「ハマった」とは少し違いますが取り上げておきます。
最初らへんの「開発コンテキスト」の項でも書きましたが、JavaScript 絡みで頼れる人がいないという状態でした。
今回は「2-3KL 程度の計算処理メインのシンプルなアプリ」だったので大した苦労もありませんでしたが、もっと本格的なアプリになってくるとこうはいかなかったはず。そもそも今回は CSS もバックエンドもノータッチなわけですし……
そうでなくとも上記の反省やらハマりポイントやらを見れば、いかに遠回り or 非効率的な道を歩んでいるかがわかります。これが、詳しい人がいたら一言で教えてもらえたりするわけで……やはり人って大事ですよね。
おわりに
以上、JavaScript ES5 を使った小規模アプリ開発から得た知見や感想をまとめてみました。
まだまだ「Web アプリを開発した」と言えるレベルではありませんが、JavaScript 事情についてほんの少しは学べたと思います。何かの参考になりましたら幸いです