JavaScriptは猛省すべき
JavaScriptと関わって丸3年。
中級者になったくらいの気分。
うっかりはまった落とし穴を記す。
後続の人はよく避けられたい。
ES6は妥当
ReferenceError: * is not defined
- コールバック関数中のthisで
- 原因: コールバック関数が異なるスコープであり、thisが切り替わってしまうため
- ES6で解決: アロー関数: ()=>{}
- 反省点: bindしたりしなかったりの記述の不統一さや、よく使う式なのに記述タイプ量が多いこと
従来の書き方では、引数で渡すかbindしていた。あるいは冒頭にvar self = this; とか。
arr.forEach(function(){}, this)
arr.reduce(function(){}.bind(this), initialValue)
なお、引数リスト内で分割代入ができるらしいが、使えて便利な頻度はどれくらいか?
// 引数リスト内の分割代入もサポートしています
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
- 連想配列のループで
- 原因: for-inループはenumerableなプロパティを拾うため
- ES6で解決: for-of, Map
- 反省点: 仕様の矛盾でしょう
従来の書き方では、意図通りに使うにはObject.keys()を使う必要があった。
Object.keys(hash).map(function(key){val = hash[key]; ...})
基底のObjectをそう簡単に拡張するなよ、と思う向きもあるかもわからんが、Objectの備える基礎メソッドが貧弱だから追加せざるをえないという。for...ofを見よ。
Object.keys(hash)ではなくhash.keys()にするには
Object.defineProperty(Object.prototype, 'keys', {
enumerable: false,
value: function() {
return Object.keys(this);
}
});
ただ、for-ofはargumentsやNodeListなどもループで回せるから改善されたように思えるが、文法要素を増やす対応が適切かは個人的には疑問。コンパイラの修正・検証・最適化に対する指示を考えると。
パッケージで解決
- 文字列へのフォーマット関数sprintf()
prototypeに追加せざるを得ない
Array.prototype._push():Array
- ループ中に除外する場合、記述が定型的に冗長になる
- 原因: Array.prototype.push()の戻り値がlength
var result_caseofinloop = arr.reduce(function(acc, val){
if(cond)
acc.push(f(val));
return acc
}, [initialValue])
// そうではなく短く書きたいねん
var result_caseofinloop = arr.reduce((acc,val)=>{
return cond ? acc._push(f(val)): acc
}, [initialValue])
// otherwise 除外してループというやり方もある。
// initialValueがなければいいけどな。unshiftも結果配列のlength返すという、、、もー。
var result_caseofbeforeloop = arr.filter(cond).map((val)=>{return f(val)}
result_caseofbeforeloop.unshift(initialValue);
// 性能的にどうなんやろ?
//
Object.prototype._assign(key, val): Object
- ループ中の記述が定型的に冗長になる
- 原因: 代入式の戻り値が右辺だから
// hashのキーでループ
var result_caseofinloop = Object.keys(hash).reduce(function(acc, key){
acc[key] = f(hash[key])
return acc
}, initialValue)
// 短く書きたいねん
var result_caseofinloop = hash.keys().reduce((acc, key)=>{
return acc._assign(key, f(hash[key]))
}, initialValue)
// 除外条件付きなら、keys(cond)にするのも手やな。
var result_caseofinloop = hash.keys(cond).reduce((acc, key)=>{
return acc._assign(key, f(hash[key]))
}, initialValue)