Function.prototype.call(thisArg [, arg1, arg2, ...])
概述
call()
方法调用一个函数,其具有一个指定的this
值和分别提供的参数(参数的列表)。
当第一个参数为null、undefined
时, 默认this
上下文指向window
。
简单实例
const name = 'qianyin';
const product = {
name: 'linheng',
};
function log(...args){
console.log(this.name, ...args);
}
log(1, 2, 3); // qianyin 1 2 3
log.call(null, 1, 2, 3); // qianyin 1 2 3
log.call(product, 1, 2, 3); // linheng 1 2 3
对箭头函数无效
const name = 'qianyin';
const product = {
name: 'linheng',
};
const log = (...args) => {
console.log(this.name, ...args);
}
log(1, 2, 3); // qianyin 1 2 3
log.call(null, 1, 2, 3); // qianyin 1 2 3
log.call(product, 1, 2, 3); // qianyin 1 2 3
箭头函数作为函数的一种形式, 对于this
的处理和普通函数有所区别, 其没有自己的this
上下文,也就是说通过bind/call/apply
函数方法设置this
值时无效的,会被忽略。
因为箭头函数没有自己的this
上下文, 所以箭头函数的this
上下文等于定义函数处的this
上下文,也就是最近的一个this
上下文。
你可以认为箭头函数的this
和调用者无关,只和其定义时所在的this
上下文相关。
如下代码: 在对象obj
中使用箭头函数定义log
函数, 那么因为箭头函数没有自己的this
上下文, 所以log
函数的this
上下文等于定义箭头函数处的this
上下文, 等于对象obj
所处的this
上下文(window
)。
const name = 'linheng';
const obj = {
name: 'qianyin',
log: () => {
console.log(this.name);
}
};
obj.log(); // linheng
在obj
中定义一个log
函数并且使得this
指向对象obj
的方法:
const name = 'linheng';
const obj = {
name: 'qianyin',
log: function(){
console.log(this.name);
}
};
obj.log(); // qianyin
Function.prototype.apply(thisArg [, Array])
概述
apply()
方法 调用一个具有给定this
值的函数,以及作为一个数组(或类似数组对象)提供的参数call()
方法的作用和apply()
方法类似,区别就是除了第一参数call()
方法接受的是参数列表 ,而apply()
方法接受的是一个参数数组(或类数组)。
简单实例
var name = 'qianyin';
var product = {
name: 'linheng',
};
function log(...args){
console.log(this.name, ...args);
}
log([1, 2, 3]); // qianyin [1 2 3]
log.apply(null, [1, 2, 3]); // qianyin 1 2 3
log.apply(product, [1, 2, 3]); // linheng 1 2 3
对箭头函数无效
const name = 'qianyin';
const product = {
name: 'linheng',
};
const log = (...args) => {
console.log(this.name, ...args);
}
log([1, 2, 3]); // qianyin [1 2 3]
log.apply(null, [1, 2, 3]); // qianyin 1 2 3
log.apply(product, [1, 2, 3]); // qianyin 1 2 3
Function.prototype.bind(thisArg [, arg1, arg2, ...])
概述
bind()
方法创建(拷贝)一个新的函数 , 当这个新函数被调用时this
指向thisArg
,其参数列表前几项值为创建时指定的参数序列。
thisArg
: 绑定函数被调用时,该参数会作为原函数运行时的this
指向。当使用new
操作符调用绑定函数时,该参数无效。
bind() 绑定this上下文
bind()
最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this
上下文。
JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,却又希望方法中的this
是原来的对象(比如在回调中传入这个方法)。
如果不做特殊处理的话,一般会丢失原来的对象。从原来的函数和原来的对象创建一个绑定函数,则能很漂亮地解决这个问题:
如果只是单纯绑定this
上下文, 完全可以使用箭头函数进行替代。
// 例一
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81 (通过对象调用函数, 上下文为该对象)
var retrieveX = module.getX; // 获取对象中函数的引用地址
retrieveX(); // 返回 9, 在这种情况下, "this" 指向全局作用域(在全局对象下调用函数)
// 永久为函数 boundGetX 绑定 this 上下文
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81 (函数 this 上下文永久绑定为 module)
// 例二为回调函数绑定 this 上下文
var x = 10;
var obj = {
x: 20,
get: ffunction(){
console.log(this.x);
}
};
// 将对象中方法取出(函数的引用地址),作为回调函数, 又因为 setTimeout 回调函数执行的上下文是 window
setTimeout(obj.get, 1000); // 打印 10
// 将对象中方法取出(函数的引用地址),作为回调函数并绑定 this 上下文
setTimeout(obj.get.bind(obj), 1000); // 打印 20
为函数永久绑定固定参数
bind()
的另一个最简单的用法是使一个函数拥有预设的初始参数。
这些参数(如果有的话)作为bind()
的第二个参数跟在this
(或其他对象)后面。
之后它们会被插入到目标函数的参数列表的开始位置 ,传递给绑定函数的参数会跟在它们的后面。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 为拷贝 list 方法并绑定初始参数
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
apply call bind 的一些运用
类数组转为数组
// 方法一:
const obj = {0: 'q', 1: 'i', 2: 'q', 3: 'a', 4:'n', 5: 'y', 6:'i', 7:'n', length: 8};
const arr = [];
Array.prototype.push.apply(arr, obj);
console.log(arr); // ["q", "i", "q", "a", "n", "y", "i", "n"]
// 方法二:
const obj = {0: 'q', 1: 'i', length: 2};
const arr = Array.prototype.slice.call(obj); // [q, i]
为伪数组添加新的元素
// 方法一: 当然你也可以使用 apply
const obj = {0: 'q', length: 1};
Array.prototype.push.call(obj, 'i', 'a', 'n');
console.log(obj); // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
// 方法二:
const obj = {0: 'q', length: 1};
const push = Array.prototype.push.bind(obj);
push('i', 'a', 'n');
console.log(obj); // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
求数组中最大值(最小值一样做法)
const arr = [1,2,3,4,5,6];
const max = Math.max.apply(null, arr);
// 或 const max = Math.max.call(null, ...arr)
console.log(max); // 6
数组合并追加
const arr = [1, 2];
const brr = [3, 4];
Array.prototype.push.apply(arr, brr);
// 或者 Array.prototype.push.call(arr, ...brr);
// 当然还可以这样 arr.push(...brr);
console.log(arr); // [1, 2, 3, 4]
总结
- 当我们使用一个函数需要改变
this
指向的时候才会用到call()、apply()、bind()
当然也别忘记了箭头函数 -
call()
和apply()
是对函数的调用,在调用的同时绑定this
上下文并传递参数列表 -
bind()
是对函数的拷贝进一步的封装,为函数永久绑定this
上下文并赋予固定参数 -
call()
和bind()
以参数列表形式给函数指定参数,apply()
则以数组的形式给函数指定参数