bind(call,apply和bind的区别)
本文同步本人掘金平台的文章
https://juejin/post/6844903859886751758
在JavaScript中,如果想要改变当前函数调用的上下文对象的时候,我们都会联想到call、apply和bind。比如下面
var name = 'window name';
var obj = {
name: 'call_me_R'
};
function sayName(){
console.log(this.name);
}
sayName(); // window name
sayName.call(obj); // call_me_R
复制代码
那么,call, apply和bind有什么区别呢?
在说区别之前,先简单的说下三者的共同之处吧
- 都是用来改变函数的this对象的指向
- 第一个参数都是this要指向的对象
- 都可以利用后续参数进行传参
下面说下区别
参数的传递
参考下MDN web docs -- Function:
call方法传参是传一个或者是多个参数,第一个参数是指定的对象,如开篇的obj。
func.call(thisArg, arg1, arg2, ...)
复制代码
apply方法传参是传一个或两个参数,第一个参数是指定的对象,第二个参数是一个数组或者类数组对象。
func.apply(thisArg, [argsArray])
复制代码
bind方法传参是传一个或者多个参数,跟call方法传递参数一样。
func.bind(this.thisArg, arg1, arg2, ...)
复制代码
简言之,call和bind传参一样;apply如果要传第二个参数的话,应该传递一个类数组。
调用后是否立执行
call和apply在函数调用它们之后,会立即执行这个函数;而函数调用bind之后,会返回调用函数的引用,如果要执行的话,需要执行返回的函数引用。
变动下开篇的demo代码,会比较容易理解
var name = 'window name';
var obj = {
name: 'call_me_R'
};
function sayName(){
console.log(this.name);
}
sayName(); // window name
sayName.call(obj); // call_me_R
sayName.apply(obj); // call_me_R
console.log('---divided line---');
var _sayName = sayName.bind(obj);
_sayName(); // call_me_R
复制代码
在笔者看来,call, apply 和 bind的区分点主要是上面的这两点,欢迎有想法的读者进行补充~
手写call, apply, bind方法
这里是简单的实现下相关方法的封装,为了简洁,我这里尽量使用了ES6的语法进行编写,详细的参考代码可以直接戳airuikun大牛的
airuikun/Weekly-FE-Interview issues。
call方法实现
在上面的了解中,我们很清楚了call的传参格式和调用执行方式,那么就有了下面的实现方法
Function.prototype.call2 = function(context, ...args){
context = context || window; // 因为传递过来的context有可能是null
context.fn = this; // 让fn的上下文为context
const result = context.fn(...args);
delete context.fn;
return result; // 因为有可能this函数会有返回值return
}
复制代码
我们来测试下
var name = 'window name';
var obj = {
name: 'call_me_R'
};
// Function.prototype.call2 is here ...
function sayName(a){
console.log(a this.name);
return this;
}
sayName(''); // window name
var _this = sayName.call2(obj, 'hello '); // hello call_me_R
console.log(_this); // {name: "call_me_R"}
复制代码
apply方法实现
apply方法和call方法差不多,区分点是apply第二个参数是传递数组:
Function.prototype.apply2 = function(context, arr){
context = context || window; // 因为传递过来的context有可能是null
context.fn = this; // 让fn的上下文为context
arr = arr || []; // 对传进来的数组参数进行处理
const result = context.fn(...arr); // 相当于context.fn(arguments[1], arguments[2], ...)
delete context.fn;
return result; // 因为有可能this函数会有返回值return
}
复制代码
同样的,我们来测试下
var name = 'window name';
var obj = {
name: 'call_me_R'
};
// Function.prototype.apply2 is here ...
function sayName(){
console.log((arguments[0] || '') this.name);
return this;
}
sayName(); // window name
var _this = sayName.apply2(obj, ['hello ']); // hello call_me_R
console.log(_this); // {name: "call_me_R"}
复制代码
bind方法实现
bind的实现和上面的两种就有些差别,虽然和call传参相同,bind被调用后返回的是调用函数的指针。那么,这就说明bind内部是返回一个函数,思路打开了
Function.prototype.bind2 = function(context, ...args){
var fn = this;
return function () { // 这里不能使用箭头函数,不然参数arguments的指向就很尴尬了,指向父函数的参数
fn.call(context, ...args, ...arguments);
}
}
复制代码
我们还是来测试一下
var name = 'window name';
var obj = {
name: 'call_me_R'
};
// Function.prototype.bind2 is here ...
function sayName(){
console.log((arguments[0] || '') this.name (arguments[1] || ''));
}
sayName(); // window name
sayName.bind2(obj, 'hello ')(); // hello call_me_R
sayName.bind2(obj, 'hello ')('!'); // hello call_me_R!
复制代码
美滋滋,成功地简单实现了call、apply和bind的方法,那么你可能会对上面的某些代码有疑问❓
疑惑点
1. 问call中为什么说 context.fn = this; // 让fn的上下文为context 呢?
答
我们先来看看下面这段代码--
var name = 'window name';
var obj = {
name: 'call_me_R',
sayHi: function() {
console.log('Hello ' this.name);
}
};
obj.sayHi(); // Hello call_me_R
window.fn = obj.sayHi;
window.fn(); // Hello window name
复制代码
嗯,神奇了一丢丢,操作window.fn = obj.sayHi;改变了this的指向,也就是this由指向obj改为指向window了。
简单来说this的值并不是由函数定义放在哪个对象里面决定的,而是函数执行时由谁来唤起来决定的。
2. 问bind中返回的参数为什么是传递(context, ...args, ...arguments), 而不是(context, ...args)呢?
答
这是为了包含返回函数也能传参的情况,也就是bind()()中的第二个括号可以传递参数。
call和apply哪个好?
据调查--call和apply的性能对比,在分不同传参的情况下,call的性能是优于apply的。不过在现代的高版本浏览器上面,两者的差异并不大。
而在兼容性方面,两者都好啦,别说IE了哈。
在使用的方面还是得按照需求来使用call和apply,毕竟技术都在更新。适合业务的就是最好的~囧
参考
MDN web docs -- Function
airuikun/Weekly-FE-Interview issues
《JavaScript高级程序设计》
田径项目
- 乐福与米切尔共贺生日,未来携手并进 双星汇聚
- 迪马:菲尔克鲁格将会加盟西汉姆联,米兰已经
- B席加盟曼城表现引关注,若昂内维斯实力展现征
- 霍福德谈球队风格转变钟爱防守型球队的独特魅
- 库兹马湖人挑战赛季高峰团结协作显实力,共赴
- 铁林回应传闻交易传闻与我常在,我持平常心态
- 太阳老板支持KD和布克为国征战期待金牌荣耀
- 哈姆掌湖人新程显活力,珍妮巴斯领航未来启
- 诺维茨基复出,球队遭遇两连败挫折与反思的背
- 考辛斯9分难救主上海横沙誉民力克无锡豪取小组
- 欧文骑士球衣骑士店大促销,高达75折优惠来袭
- 哈登与76人交易内幕交易延期,从十天到四个半月
- 2023至2024女排超级联赛赛程
- 没想到姆巴佩会在决赛前自宣,这可能会影响到
- 火箭队更新动态杰伦格林备战归来,未来三场有
- 奥拉迪波出色表现难挽败局,马刺击败魔术夺五