2009年02月26日 11:30 [Edit]
javascript - uneval() for the rest of us!
うーん、それもそうなんだけど....
最速インターフェース研究会 :: JavaScriptにおけるdeep cloneまず、Object.prototypeにメソッドを生やしてしまうとfor inでキーを列挙するときにいちいちhasOwnPropertyを使わないといけなくなるので普通は使いません。
で、JSONにするのにFirefoxだとtoSourceというのが使える。unevalというラッパーがあって、これだとnullでも平気。
そのuneval()が他にないので、作ってみた。
初出2007.11.27; 追記2008.06.14, 2009.02.26
文字列のEscapeをFirefox互換に。あと、
にもあるので手を入れたい方は是非。
-
Source:
- stdout:
- stderr:
今度は、Built-in Objects には一切手を触れない方向で。
ソースはこちら。見ての通り、プロトタイプを使わないと、動作を切り替える際にlinear searchしなきゃいけなくて少し泥臭い(オブジェクトのIDを取れれば、それをキーにしてディスパッチテーブルを作ることもできるのだけどなあ....)。ちょっと工夫してあるのは、uneval_set(proto, name, func)でserializerをインストールできるようにしてあるところ。ユーザー定義オブジェクトでもserializerを登録することでserializeできる。
/*
* $Id: uneval.js,v 0.4 2011/12/15 03:06:51 dankogai Exp dankogai $
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
(function(global){
if (typeof(global['uneval']) !== 'function') {
var hasOwnProperty = Object.prototype.hasOwnProperty;
var protos = [];
var char2esc = {'\t':'t','\n':'n','\v':'v','\f':'f','\r':'\r',
'\'':'\'','\"':'\"','\\':'\\'};
var escapeChar = function(c){
if (c in char2esc) return '\\' + char2esc[c];
var ord = c.charCodeAt(0);
return ord < 0x20 ? '\\x0' + ord.toString(16)
: ord < 0x7F ? '\\' + c
: ord < 0x100 ? '\\x' + ord.toString(16)
: ord < 0x1000 ? '\\u0' + ord.toString(16)
: '\\u' + ord.toString(16)
};
var uneval_asis = function(o){ return o.toString() };
/* predefine objects where typeof(o) != 'object' */
var name2uneval = {
'boolean':uneval_asis,
'number': uneval_asis,
'string': function(o){
return '\''
+ o.toString()
.replace(/[\x00-\x1F\'\"\\\u007F-\uFFFF]/g, escapeChar)
+ '\''
},
'undefined': function(o){ return 'undefined' },
'function':uneval_asis
};
var uneval_default = function(o, np){
var src = []; // a-ha!
for (var p in o){
if (!hasOwnProperty.call(o, p)) continue;
src[src.length] = uneval(p) + ':' + uneval(o[p], 1);
};
// parens needed to make eval() happy
return np ? '{' + src.toString() + '}' : '({' + src.toString() + '})';
};
uneval_set = function(proto, name, func){
protos[protos.length] = [ proto, name ];
name2uneval[name] = func || uneval_default;
};
uneval_set(Array, 'array', function(o){
var src = [];
for (var i = 0, l = o.length; i < l; i++)
src[i] = uneval(o[i]);
return '[' + src.toString() + ']';
});
uneval_set(RegExp, 'regexp', uneval_asis);
uneval_set(Date, 'date', function(o){
return '(new Date(' + o.valueOf() + '))';
});
var typeName = function(o){
// if (o === null) return 'null';
var t = typeof o;
if (t != 'object') return t;
// we have to lenear-search. sigh.
for (var i = 0, l = protos.length; i < l; i++){
if (o instanceof protos[i][0]) return protos[i][1];
}
return 'object';
};
uneval = function(o, np){
// if (o.toSource) return o.toSource();
if (o === null) return 'null';
var func = name2uneval[typeName(o)] || uneval_default;
return func(o, np);
}
}
if (typeof(global['clone']) !== 'function') {
clone = function(o){
try{
return eval(uneval(o));
}catch(e){
throw(e);
}
};
}
})(this);
Enjoy!
Dan the Javascripter
追記2009.02.26:少しだけ手直し。
Posted by dankogai at 11:30│Comments(0)│TrackBack(0)