prototype汚染問題でhasOwnPropertyを使わないクロスブラウザな方法
いちいちhasOwnPropertyを使わなくてよくする(ジェネレータの使いかた) - 素人がプログラミングを勉強していたブログについて。
2回ループするid:javascripter にジェネレータ使ってるから「2回」ってことはないとツッコマレタ。その通りですね。のは気になるのと、クロスブラウザにしつつ、微妙な高速化*1。
元ネタは指定したエレメントを非表示にするuser js書いた - <s>gnarl,</s>技術メモ”’<marquee><textarea>¥で使われていた関数から。
//適当にArrayを拡張 Array.prototype.del = function(index){ return this.slice(0,index).concat(this.slice(index+1)) }; var a = [1,2,3]; var __A = []; // 空の配列 for(var i in a) if (__A[i] !== a[i]) { // 空の配列と同じ値を持っていたら、prototypeと判定 console.log("i:" + i + " v:" + a[i]); } for(var i in a) if (!__A[i]) { // そもそも、空の配列は正の値を返さない console.log("i:" + i + " v:" + a[i]); }
前者のほうが厳密で、後者はArray.prototype.notArray = false みたいになっているとミスる。けど、実用的には問題ないだろうし、速度的には後者のほうが速い。
ちなみにhasOwnPropertyはSafari1.3がサポートしていないので、この方法はクロスブラウザってことになる。なんだけど、Safari1.3をサポートする必要は滅多にない。
Firebugでのベンチ
var a = new Array(1000).join(',').split(','); function own(o) (o[i] for(i in o)if(o.hasOwnProperty(i))); console.log(a.length); console.time("native"); a.forEach(function(){ for(var i in a) { a[i]; } }); console.timeEnd("native"); console.time("hasOwnProperty:"); a.forEach(function(){ for(var i in a) if (a.hasOwnProperty(i)) { a[i]; } }) console.timeEnd("hasOwnProperty:"); console.time("hash"); a.forEach(function(){ var __A = []; for(var i in a) if (__A[i] !== a[i]) { a[i]; } }) console.timeEnd("hash"); console.time("hash!"); a.forEach(function(){ var __A = []; for(var i in a) if (!__A[i]) { a[i]; } }) console.timeEnd("hash!"); console.time("own"); a.forEach(function(){ for(var i in own(a)) { a[i]; } }); console.timeEnd("own");
ジェネレータは遅くみえるけど、新しい機能だからってことで。最適化が進めば最速になるポテンシャルを秘めているはずです、きっと。
prototypeの一時削除と復元
おまけで、prototype汚染を一時的になくし、元に戻すってネタ。
function cleanUpPrototype(str){ if (!window[str]) return; var o = new window[str]; var dty = []; for (var k in o) { dty.push( [k, o[k] ] ); delete window[str].prototype[k]; } var fn = revertPrototype; fn.cache = fn.cache || {strs:[]}; fn.cache.strs.push(str); fn.cache[str] = dty; } function revertPrototype(){ var fn = revertPrototype; if (!fn.cache && !fn.cache.strs.length) return; var cache = fn.cache; var strs = cache.strs; for (var i = 0;i < strs.length;i++){ var str = strs[i]; var dty = cache[str]; for (var j = 0;j<dty.length;j++){ window[str].prototype[dty[j][0]] = dty[j][1]; } } fn.cache = null; } Array.prototype.del = function(index){ return this.slice(0,index).concat(this.slice(index+1)) }; var a = [1,2,3,4,5]; a = a.del(1); for(var i in a) { console.log("native:" + i + " v:" + a[i]); } cleanUpPrototype("Array"); for(var i in a) { console.log("native:" + i + " v:" + a[i]); } revertPrototype(); for(var i in a) { console.log("native:" + i + " v:" + a[i]); } a = a.del(1);