2013年03月18日 18:30 [Edit]

javascript - es2pi はじめました

es2piというライブラリーをこつこつと書き続け始めました。


What?

ES6のpolyfillと、それでも足りないと私が日頃感じていた機能を実装したJavaScript Libraryです。

2πという名前に、ES6に対する割り切れない想いを託しました。

Demo

とりあえずES5でPolyfillできるES6の機能までは実装ずみです。Polyfill以外でも関数よりもメソッドの方の方がふさわしいと思われる機能も一部実装してあります。その他の機能は徐々に追加して行く予定。

log('dan'.repeat(5)     );      // in ES6 proposal
log(['dan'].repeat(5)   );      // NOT in ES6
log('-'.repeat(16))             // I love it!
log(isNaN('dankogai')    );     //
log(Number.isNaN('dankogai'));  // in ES6;
log((0/0).isNaN())              // NOT in ES6;
log((1/0).isNaN())              // ditto;
log((1/0).isFinite())           // ditto;
log(Number.toInteger(Math.PI))  // in ES6;
log(Math.PI.toInteger())        // NOT in ES6;
log('-'.repeat(16))
log(Object.isNil(null));
log(Object.isNil(undefined));
log(Object.isNil(''));
log(Object.isNil(false));
log(Object.isNil(0));
log('-'.repeat(16))
log(''.isNil());
log(''.isPrimitive());
log(''.isString());
log('-'.repeat(16))
log([].isArray());
log({}.isObject());
log('-'.repeat(16))
var p = document.createElement('div');
log(p.typeOf());
log(p.classOf());
log('-'.repeat(16))
log(Object.keys(Object.prototype)); // thanks to ES5, this is blank!
Object.getOwnPropertyNames(Object.prototype)
    .filter(function(k){ return ! k.match(/^__/) })
    .forEach(function(k){
        if (!Object[k].isFunction()) return;
        if ( Object[k].isBuiltIn() ) return;
        log('Object.prototype.' + k);
    });

Too Frequently Used Functions not to include

これらはライブラリー書く時にはほぼ必ず使うので、もう追加しちゃっていいでしょう。

var o = {name:"dankogai", lang:"perl"};
Object.extend(o, {lang:"javascript"});
log(o);
Object.defaults(o, {lang:"PHP", ver:5});
log(o);

Deep Comparison and Cloning

js-object-cloneと同様です。

var src = { name: 'dankogai', lang: ['perl'] };
var dst = Object.clone(src);        // shallow copy
log( Object.is(src, dst));          // false
log( Object.equals(src, dst) );     // true
dst.lang.push('javascript');
log(src.lang                    );  // ["perl","javascript"] because dst is shallow-copied
dst = Object.clone(src, true);      // deep copy
dst.lang = dst.lang.reverse();
log( src.lang                   );  // ["perl","javascript"]
log( dst.lang                   );  // ["javascript","perl"]
var src = new Date(0);
var dst = Object.clone(src, true)
log( src === dst                );
log( Object.equals(src, dst)    );
log( dst                        );
src = /./g;
dst = Object.clone(src, true);
log( src === dst                );
log( Object.equals(src, dst)    );
log( dst                        );
var Point = function(x, y) {
    if (!(this instanceof Point)) return new Point(x, y);
    this.x = x*1;
    this.y = y*1;
};
Point.prototype = {
    distance: function(pt) {
        if (!pt) pt = Point(0,0);
        var dx = this.x - pt.x;
        var dy = this.y - pt.y;
        return Math.sqrt(dx*dx + dy*dy);
    }
};
var src = Point(3,4);
var dst = Object.clone(src, true);
log( src === dst              );   // false
log( Object.equals(src,dst)   );   // true
log( dst.distance(Point(0,0)) );   // 5

Map and Set

Mapは文字列以外もキーになる「ハッシュ」。Perlの Tie::RefHash やRubyのHashやPythonのdictionaryと同様です。

ECMAScript 6のリファレンス実装ではなんと配列を線形探索していて、Web上でも見つかるPolyfillのほとんどもこれに準ずるのですが、本実装ではキーがプリミティブの場合には配列の線形探索を避けるようにしてあります。

その部分だけ抜き出したものも

として用意してあります。

log('--Map --'.repeat(8));
var a = [], o = {}, m = Map();
[undefined, null, false, 0, a, o].forEach(function(v){
    m.set(v, v);
    log(m.get(v));
});
log(m.get(undefined) === m.get(''))        // false
log(m.get(undefined) === m.get(undefined)) // true
log(m.has([]));     // false;
log(m.has(a));      // true;
a.push(false);
log(m.get(a));      // [0]
log(m.has({}));     // false;
log(m.has(o));      // true;
o[undefined] = null;
log(m.get(o));      // {undefined:null}
log(m.items());     // JSON.strigify() converts undefined to null orz
var s = Set();
log('--Set --'.repeat(8));
[undefined, null, false, 0, a, o].forEach(function(v){
    s.add(v);
    log(s.has(v));
    log(s.size);
    s.add(v);
    log(s.size);
});
log(s.values());

Why?

結局 I can't get no satisfaction ちゅうことです。

  • たとえばなんで String.prototype.repeat が入ったのに Array.prototype.repeat がないの?いつまで私たちはArray.apply(null, Array(8))ってドヤ顔したあげく、いたいけな初心者を置いてけぼりにすればよいのでしょう?こんなの[null].repeat(8)でいいでしょうに。
  • メソッドが生えない無毛で不毛なnullundefinedは仕方がないにしても、それ以外のオブジェクトはもっと自分のことをメソッドで語ってもいいでしょう。typeof o === 'number'よりもo.isNumber()の方がずっと楽ですし。(本当はgetterにして()も省きたかったけど、今は堪えてる)
  • せっかくES5Object.prototypeを人類に解放してくれたのに、これを使わぬ手はありません。
  • おそらくES5に最も影響を与えたのはPrototype.jsですが、ES5の到着が遅すぎて、結局到着するころに繁栄したのはBuilt-in Prototypesを拡張しない代わりに一文字ネームスペースを事実上乗っ取ったjQueryUnderscore.jsだったというのは悲しすぎる。noConflictなんてただの飾りです。えらい人にはそれがわからんのです。
mofmof-js - mofmof mofmof - Google Project Hosting
本来の JavaScript の利用方法(Prototype 拡張)に立ち返り、Array.prototype, String.prototype, Number.prototype 等を拡張しています

ほんと、120%同意。Built-In Objects の Properties の descriptor が {enumerable: false, writable:true, configurable:ture} となっていることに、もう一度思いを致すべきではないの?

というわけで、そのまま使っていただいても構わないのですし、そうしてもらいたいこともあって今回ははじめてminified versionも同封してあるのですが、むしろ「ぼくのかんがえるさいていげんぶんかてきなJavaScript」のたたき台として使ってもらうことを望んでいます。

Forks welcome! Pull requests welcome!!

Dan the JavaScripter Who Can't Get No Satisfaction


この記事へのトラックバックURL