call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数。主要用来改变函数运行时this的指向
基本用法
1、call方法
基本语法:func.call(thisArg, param1, param2, …)
call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
示例:
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.call(obj, 'wang', 'xiaoming')//My name is: wang xiaoming
2、apply
基本语法:func.apply(thisArg, [param1,param2,…])
apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
示例:
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.apply(obj, ['wang', 'xiaoming'])//My name is: wang xiaoming
3、bind
基本语法:func.bind(thisArg, param1, param2, …)
和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法不是马上执行,返回值是函数。而call、appll是在改变了函数的 this 指向之后立马执行。
var obj = {
name: 'wang xiaoming'
}
function printName() {
console.log(this.name)
}
var Name = printName.bind(obj)
console.log(Name) // function () { … }
Name() // wang xiaoming
为了更好地理解,我们结合一段代码再深入理解一下这几个方法。
let a = {
name: 'jack',
getName: function(msg) {
return msg + this.name;
}
}
let b = {
name: 'lily'
}
console.log(a.getName('hello~')); // hello~jack
console.log(a.getName.call(b, 'hi~')); // hi~lily
console.log(a.getName.apply(b, ['hi~'])) // hi~lily
let name = a.getName.bind(b, 'hello~');
console.log(name()); // hello~lily
从上面的代码执行的结果中可以发现,使用这三种方式都可以达成我们想要的目标,即通过改变 this 的指向,让 b 对象可以直接使用 a 对象中的 getName 方法。从结果中可以看到,最后三个方法输出的都是和 lily 相关的打印结果,满足了我们的预期。
区别:
方法 | call | apply | bind |
---|---|---|---|
参数 | 多个 | 一个数组 | 多个 |
功能 | 函数调用改变this | 函数调用,改变this | 函数调用,改变this |
结果 | 直接执行 | 直接执行 | 返回数组 |
应用场景
1、判断数据类型
function getType(obj){
let type = typeof obj;
if (type !== "object") {
return type;
}
return Object.prototype.toString.call(obj).replace(/^$/, '$1');
}
2、类数组借用方法
var arrayLike = {
0: 'java',
1: 'script',
length: 2
}
Array.prototype.push.call(arrayLike, 'jack', 'lily');
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
3、获取数组的最大 / 最小值
let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6
自己实现这些方法
1、手写实现 call()
Function.prototype.myCall = function (context, ...args) {
var context = context || window;
context.fn = this; //this是当前函数
var result = eval('context.fn(...args)'); //执行完函数,并将结果返回
delete context.fn //清理掉fn,防止污染
return result;
}
2、手写实现 apply()
Function.prototype.myApply = function (context, args) {
//args是类数组
var context = context || window;
context.fn = this; //this是当前函数
var result = eval('context.fn(...args)'); //执行完函数,并将结果返回
delete context.fn //清理掉fn,防止污染
return result;
}
3、手写实现 bind()
Function.prototype.myBind = function (context,...bindArgs) {
// this为调用mybind的函数。将this赋值给变量_this
let _this = this;
// 返回函数fn
return function fn(...args){
//拼接参数
const newArgs = bindArgs.concat(args)
// 通过apply方法调用函数并返回结果。
return _this.apply(context,newArgs);
}
}