LoginSignup
5

More than 5 years have passed since last update.

コンストラクタに配列を展開して渡したい

Last updated at Posted at 2013-10-01

Arrayっぽいこんなクラスがあったとする

function Arraylike() {
    for (var i = 0, len = arguments.length; i < len; i++) {
        this[i] = arguments[i];
    }
    this.length = len;
}

Arraylike.prototype.join = function(separator) {
    return [].join.call(this, separator);
}

// new Arraylike(1, 2, 3).join(', ') => "1, 2, 3"

var array = [1, 2, 3] みたいな配列があって、これをArraylikeのコンストラクタに渡したいとき、どうしたらよいのか。

簡単にできそうでできないのでわりと困るけど、何個かそれっぽいやりかたはある。

Function.prototype.bind を使う方法

ES5で定義されたFunction.prototype.bindを使えば、実はうまいことできる。

var Binded = Arraylike.bind.apply(Arraylike, [null].concat(array));
var instance = new Binded();

console.log(instance.join(', ')); //=> "1, 2, 3"
console.log(instance instanceof Arraylike); //=> true

ただし、この方法はちょっと古い iOS や Android、IE8では使えない模様。

http://kangax.github.io/es5-compat-table/
http://d.hatena.ne.jp/zentoo/20120819/1345378440

ダミーのコンストラクタを作る方法

プロトタイプを空のコンストラクタに付け替える方法。
ただし、この方法では組み込みクラスで使えなかったりする。

function Dummy() {}
Dummy.prototype = Arraylike.prototype;
var instance = new Dummy();
Arraylike.apply(instance, array);

console.log(instance.join(', ')); //=> "1, 2, 3"
console.log(instance instanceof Arraylike); //=> true

evalする方法

弾先生のブログにヒントが
http://blog.livedoor.jp/dankogai/archives/51758978.html

var argsValue = array.map(function(v, i){
    return 'args[' + i + ']';
}).join(', ');

var instance = Function('ctor', 'args',
    'return new ctor(' + argsValue + ');')(Arraylike, array);

console.log(instance.join(', ')); //=> "1, 2, 3"
console.log(instance instanceof Arraylike); //=> true

結論

evalする方法が一番対応ブラウザ多くて素直だと思う。
ネックは速度だろうけど、生成した関数をキャッシュしておけば解決できそう。
Functionにメソッド追加して、こんな感じでどうでしょうか。

なにか問題があったり、もっとスマートなやり方があれば教えてください。

Object.defineProperty(Function.prototype, 'applyConstructor', (function(){
    var cache = {};

    function generate(len) {
        var argsValue = [];
        for (var i = 0; i < len; i++) {
            argsValue.push('args[' + i + ']');
        }
        return Function('ctor', 'args', 'return new ctor(' + argsValue.join(',') + ')');
    }

    return {
        value: function(args) {
            var len = (args != null && args.length) || 0;
            return (cache[len] || (cache[len] = generate(len)))(this, args);
        }
    };
}()));

console.log(Arraylike.applyConstructor(array).join(', ')); // => 1, 2, 3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5