·func.call([context], x, y)
·func.apply([context], [x, y])
let obj = {
fn(x, y) {
console.log(this, x, y);
}
};
obj.fn.call({}, 10, 20); // {}, 10, 20
obj.fn.apply(window, [10, 20]); //window, 10, 20
setTimeout(obj.fn.bind(30, 10, 20), 1000); //Number(30), 10, 20
先试着重写一下bind:
从参数看,首先是传递一个this指向并需要做一下处理,后续还有若干个参数
function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
}
bind函数中的this是指最终要执行的函数,而且执行bind的时候会返回一个新的匿名函数,并且在这个新的函数中执行最终要执行的函数也就是this,并且改变其this指向:
function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
//需要最终执行的函数
var _this = this;
return function anonymous() {
_this.apply(context, args);
};
}
这个bind函数大体算是写完,但还是有些问题,比如给元素进行事件绑定,div.onclick = obj.fn.bind(window, 10, 20),元素进行点击的时候,会有ev事件对象,相当于在执行bind函数返回的那个匿名函数中也需要传递参数,而且参数个数不确定,当然,最后还需要改写一下原型上的方法:
function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
//需要最终执行的函数
var _this = this;
return function anonymous() {
var amArg = [].slice.call(arguments, 0);
_this.apply(context, args.concat(amArg));
};
}
Function.prototype.bind = bind;
这样,这个bind函数的重写算是完成了。
我们用es6改写一下:
function bind(context = window, ...args) {
return (...amArg) => this.call(context, ...args.concat(amArg));
}
代码看上去确实精简不少,当然也可以用apply,但经测试,性能不如call。
接下来看看重写call:
它会有若干个参数,第一个是要指向的this,直接用es6写法:
function call(context = window, ...args) {
}
函数中的this就是要调用call方法的函数,想让该函数执行并且其内部this指向传递进来的context,那么形如context.函数可以做到:
function call(context = window, ...args) {
//给context增加一个$fn属性,把当前函数赋给这个属性
context.$fn = this;
//让context.$fn这个方法执行,就是之前this函数执行,并且this指向的是context
let result = context.$fn(...args);
//增加完方法应该删除
delete context.$fn;
return result;
}
Function.prototype.call = call;
apply也就出来了:
function apply(context = window, args) {
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
Function.prototype.apply = apply;
这里边还是有两个问题,一是$fn属性没有删除,目前还没想到解决办法,一个就是传进来的context必须是引用类型,但其实可以是基础类型:
function call(context = window, ...args) {
context === null ? context = window : null;
let type = typeof context;
if (type !== "object" && type !== "function" && type !== "symbol") {
//=>基本类型值
switch (type) {
case 'number':
context = new Number(context);
break;
case 'string':
context = new String(context);
break;
case 'boolean':
context = new Boolean(context);
break;
}
}
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
apply的判断就不写了,但基本都实现了重写,当然,这几个方法毕竟是js的内置写法,我们只是想大致实现它们的实现原理。
在处理类数组中,发现了两种将数组方法应用于类数组的方法,现将call/apply的常用方式总结一下。当做函数调用;作为对象的方法,给第三方使用;作为原型的方法,给第三方使用
面试题:如何用apply实现一个bind?bind函数在 ES5 才被加入,所以并不是所有浏览器都支持,IE8及以下的版本中不被支持,如果需要兼容可以使用 Polyfill 来实现。 bind方法与call/apply最大的区别就是bind返回一个绑定上下文的函数
相信很多朋友都会被问到过这样的一个JS问题,如何实现call | apply | bind,很多朋友只会用但是不会写,或者是死记硬背写法,等到面试官提问的时候,支支吾吾讲不清楚,今天我将教会大家完全理解这个破题!
call 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数,上述例子中,当foo函数单独调用时内部this绑定为全局对象window。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!