withみたいなことをstrictモードで!
with
ってなにげに便利なんだけど、strictモードでは使えない。
似たようなことをやる方法ないかと考えてみた。
'use strict'; myWith({hoge: 'fuga'}, function(){ console.log(hoge); });
こんなコードが動けば嬉しい。
Functionコンストラクタを使う
のっけから黒魔法使う。
関数を文字列化 → 変数宣言加える → えばる という発想。
function myWith(obj, fn){ var vars = []; for(var n in obj){ vars.push(n + '=this.' + n); } new Function('var ' + vars.join(',') + ';(' + fn.toString() + ')()').call(obj); }
ただこの方法はスコープチェーンぶった切るので、
var hoge = 'hoge'; myWith({}, function(){ console.log(hoge); //=> エラー! });
のようにmyWith
より前で宣言した変数にアクセスできない。困った。
引数として渡す
無理やりスコープチェーンに突っ込むことができないので、少々冗長になるが、引数として渡すようにする。
'use strict'; myWith({hoge: 'hoge'}, function(hoge){ console.log(hoge); });
このように呼び出せるようにする。
実装はこんな感じ。引数を解析して必要になるパラメータだけ渡す。
function myWith(obj, fn){ var argNames = fn.toString().match(/function[^(]*\(([^)]+)\)/)[1].split(','); var args = []; for(var i = 0, len = argNames.length; i < len; i++){ var name = argNames[i].replace(/^\s+|\s+/, ''); args.push(obj[name]); } fn.apply(this, args); }
思ったより便利かも
複数のオブジェクト引数にとれて、フォールバックするように実装したら便利かも。
例えばif文を使わずに古いブラウザでだけ自前で実装したObject.createを使うとか、Array.prototypeからよく使うメソッド引っこ抜くとか、両方一緒にやっちゃうとか。
var Polyfill = { create: function(){ ... } }; myWith(Object, Polyfill, [], function(create, slice){ var obj = create({}); ... });