1、call()
var a = {
user:"江苏南京",
fn:function(){
console.log(this.user); //江苏南京
}
}
var b = a.fn;
b.call(a); // 等价于直接执行a.fn();输出“江苏南京”
通过在call方法,给第一个参数添加要把b添加到哪个环境中,简单来说,this就会指向那个对象。
call方法除了第一个参数以外还可以添加多个参数,如下:
b.call(a,1,2);
2、apply()
apply方法和call方法有些相似,它也可以改变this的指向
var a = {
user:"江苏南京",
fn:function(){
console.log(this.user); //江苏南京
}
}
var b = a.fn;
b.apply(a); // 等价于直接执行a.fn();输出“江苏南京”
同样apply也可以有多个参数,但是不同的是,第二个参数必须是一个数组,如下:
b.apply(a,[10,1]);
或者
var arr = [500,20];
b.apply(a,arr);
//注意如果call和apply的第一个参数写的是null,那么this指向的是当前所在的全局对象或者window对象(具体看下面的axios源码)
b.apply(null);
call 与 apply 的区别
***call的第二部分参数要一个一个传,apply要把这些参数放到数组中 ***,就这么点区别!
源码案例🌰:
axios 源码中axios.spread()方法使用案例如下:
function f(x, y, z) {
console.log(x+y+z)
}
var args = [1, 2, 3];
f.apply(null, args);
// 输出: 6 (相当于f(1,2,3),将数组元素作为参数)
用axios.spread方法用法:
axios.spread(function(x, y, z) {})([1, 2, 3]);
源码实现:
/**
* @param {Function} callback
* @returns {Function}
*/
module.exports = function spread(callback) {
return function wrap(arr) {
return callback.apply(null, arr);
};
};
3、bind()
bind方法和call、apply方法有些不同,但它们都是用来改变this的指向。
var a = {
user:"江苏南京",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a);
我们发现代码没有被打印
var a = {
user:"江苏南京",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
var c = b.bind(a);
console.log(c); // function() { [native code] } 得到的是函数,而非执行结果
那么我们现在执行一下函数c看看,能不能打印出对象a里面的user
c(); // 江苏南京
一目了然!!! 同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。
var c = b.bind(a,10); c(1,2);
总结
*** bind()和call、apply不同。bind是新创建一个函数,然后把它的上下文绑定到bind()括号中的参数上,然后将它返回,并没有直接执行。而只是返回一个改变了上下文的函数副本,而call和apply是直接执行函数。 ***
面试常考手写bind方法
Function.prototype.bind = function(context) {
let self = this; // 原型中的this指向调用它的对象,所以此处要转存
let args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
关于这段代码let self = this,我们做个解释,看下面的代码:
function a(){};
a.prototype.sing = function(){
console.log(a.prototype == this);
};
var b = new a();
b.sing();//false
显然,this不指向prototype,而经过测试,它也不指向a,而指向b。 所以*** 原型中的this指向调用它的对象。 ***
Array.prototype.slice.call(arguments); // [1,2,3,4].slice(1) // 输出[2, 3, 4]
上面代码会将一个类数组转化为数组。由于arguments自己没有slice方法,这里属于借用Array原型的slice方法。
如果你不给slice传参数,那就等于传了个0给它,结果就是返回一个和原来数组一模一样的副本。
这之后的代码就很好理解,返回一个函数,该函数把传给bind的第一个参数当做执行上下文,由于args已经是一个数组,排除第一项,将之后的部分作为第二部分参数传给apply。
*进阶版本的手写bind可以参考若川大神的文章:https://juejin.cn/post/6844903718089916429 *
本文使用 mdnice 排版