Fn.bind.apply() 解决 new 操作符不能用与 apply 或 call 同时使用

背景:

小明想要用数组的形式为 Cls.func 传入多个参数,他想到了以下的写法:

var a = new Cls.func.apply(null, [1, 2, 3]);

然而浏览器却报错 Cls.func.apply is not a constructor。
乍一看是 new 操作符去修饰 Cls.func.apply 了,于是他又这么写:

var a = (new Cls.func).apply(null, [1, 2, 3]);

浏览器依旧报错。。。好吧,还是好好查一查相关的解决方法吧,还好这是个好时代,没有什么是网上查不出来的。


思路:

在网上找到了非常关键的几个解决方案和思路。

参考链接 http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible

关键摘抄:

function newCall(Fn) {
    return new (Function.prototype.bind.apply(Fn, arguments));
    // or even
    // return new (Fn.bind.apply(Fn, arguments));
    // if you know that Fn.bind has not been overwritten
}

// It can be used as follows:
var s = newCall(Fn, a, b, c);

// or even directly:
var a = new (Function.prototype.bind.call(Fn, null, 1, 2, 3));

var a = new (Function.prototype.bind.apply(Fn, [null, 1, 2, 3]));

以上关键就在于 .bind.apply() 或 .bind.call() 这中写法。
Function.prototype.bind() 等同于 Fn.bind() 会创建一个新的函数,第一个参数为新函数的 this 指向,而后多个参数为绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

先分析一下 Function.prototype.bind.call() 这种写法:

var a = new (Function.prototype.bind.call(Fn, null, 1, 2, 3));

call() 接受多个参数,第一个参数为函数执行的上下文环境,后面的参数会依次传递给前面的 bind 作为参数。
所以 bind() 接到的参数为 bind(null, 1, 2, 3)。所以上面的那种写法就等同于:

var a = new ( Fn.bind(null, 1, 2, 3)() );

同理再推导 Function.prototype.bind.apply() 写法:

var a = new (Function.prototype.bind.apply(Fn, [null, 1, 2, 3]);

call() 接受两个参数,第一个参数为函数执行的上下文环境,第二个参数为数组,数组的每一项会一次作为 bind() 的参数,因此 bind() 接受到的参数也为 bind(null, 1, 2, 3)。因此也等价于:

var a = new ( Fn.bind(null, 1, 2, 3)() );

解决:

有了上面的推导,小明这时候就可以轻易的写出自己想要的结果了,同时为了拓展方便,小明决定写一个通用的方法:

function newApply(Fn, argsAry) {
    argsAry.unshift(null);
    return new (Fn.bind.apply(Fn, argsAry));
}

// 调用
newApply(Cls.func, [1, 2, 3]) // well done !!

作者博客:pspgbhu http://www.cnblogs.com/pspgbhu/

原文链接:http://www.cnblogs.com/pspgbhu/p/6796795.html

作者GitHubhttps://github.com/pspgbhu

欢迎转载,但请注明出处,谢谢!

posted @ 2017-05-02 16:36  pspgbhu  阅读(1145)  评论(0编辑  收藏  举报