JavaScript中的call、apply 和 bind
1. Function.prototype.call()
函数实例的 call 方法,可以指定函数内部的 this 指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数
一句话概括 call 方法的作用:例如 a.call(obj) ,在 obj 的作用域中运行 a,
let obj = {};
let f = function() {
return this;
}
f() === window // true
f.call(obj) === obj // true
/*
上面代码可以看出,全局环境下运行函数 f,this指向全局环境,call 方法可以改变 this 指向,指定 this 指向 obj,然后在对象 obj 的作用域下运行函数 f
*/
call 方法的参数,应该是一个对象。如果参数为空、
null
和undefined
,则默认传入全局对象
let n = 123;
var obj = { n: 456 };
let a = function() {
console.log(this.n)
}
a.call() // 123
a.call(null) // 123
a.call(undefined) //123
a.call(window) // 123
a.call(obj) // 456
/*
上面代码中 call 的方法的第一个参数如果是为空、null、undefined 等同于指向全局对象 window(浏览器为 window 对象),所以输出的都是 123
call 方法的第一个参数如果是一个对象,则会子 obj 的作用域下调用函数 a ,所以输出 456
*/
如果函数第一个参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入 call 方法
function f() {
consolelog(this)
}
f.call(5) // Number{5}
f.call('a') // String{'a'}
// call 中的第一个参数是一个原始值,会自动转化为包装对象,然后绑定到 this 中
call 方法还可以接受多个参数
第一个参数:所要指向的那个对象
后面的参数:函数调用时所需的参数
function add(a,b) {
return a + b;
}
// 第一个参数 this 表示当前环境
add.call(this,1,2) // 3
/*
call 方法指定函数 add 内部的 this 指定到当前环境,并且调用函数 add 时的参数为 1,2。所以返回 3
*/
2. Function.prototype.apply()
apply 方法与 call 方法的作用类似,也是改变 this 的指向,然后在调用该函数。唯一的区别就是,apply 接受一个数组作为函数执行的参数,使用格式如下
func.apply(obj,[arg1, arg2, ...])
apply 方法的第一个参数也是 this 所要指向的对象,如果设为 null 或 undefined ,则等同于只想全局对象。第二个参数则是一个数组,该数组中的所有元素依次传入原函数。
function f(x,y) {
return x + y;
}
f.call(null, 1, 2) // 3
f.apply(null, [1, 2]) // 3
3. Function.prototype.bind()
bind() 方法用于将函数体内的 this 绑定到某个对象,然后返回一个新函数
function.bind(obj)
在 obj 的环境中运行 function 这个函数(记住这句话) ,相当于obj.function
let d = new Date()
d.getTime() // 1658922441747
let p = d.getTime
/*
d.getTime 这个方法的执行上下文是 window
换句话说就是:在 window 这个环境中运行 d.getTime,而 window 中没有 getTime 这个方法,所以报错
*/
p() // this is not a Date object.at getTime (<anonymous>)
上面代码中将 d.getTime 赋值给 p ,然后调用 p 会报错,这是因为 getTime() 方法内部的 this 绑定 Date 对象实例,赋值给 p 后,内部的 this 已经不在指向 Date 对象实例了
let p2 = d.getTime.bind(d)
/*
上面那个代码的意思是将 d.getTime 这个方法的执行上下文变为 d,而 d 是一个 Date 实例
换句话说就是在 Date 这个环境中运行 d.getTime 这个方法,而 Date 中有这个方法
*/
p2() // 1658922441747
bind() 方法将 getTime 内部的 this 绑定在 d 对象,这时就可以安全的将这个方法复制给其他变量了
bind() 方法的参数就是所要绑定的 this 对象
let counter = {
count: 0,
inc: function() {
this.count++
}
}
let func = counter.inc.bind(conuter)
func()
counter.count // 1
也可以将 this 绑定到其他对象
let counter = {
count: 0,
inc: function() {
this.count++
}
}
let obj = {
count: 100
}
let func = counter.inc.bind(obj) // 在 obj 的环境中运行 counter 里面的 inc 方法
func()
obj.count // 101
bind() 还可以接受更多的参数,将这些参数绑定到原函数的参数
let add = function(x,y) {
return x * this.n + y * this.m
}
let obj = {
n: 2,
m: 2
}
let newAdd = add.bind(obj,5) // 将 5 传递给 add 函数的 x
newAdd(6) // 22 2*5+2*6
通过
bind
绑定的函数,原函数与新函数是不相等的
const fn1 = function () {
console.log("Function1",this.a)
}
const obj = {
a: 1
}
const fn2 = fn1.bind(obj) // Function 1
fn1 // Function undefined
fn2 === fn1 // false
// 通过 bind 绑定的原函数与新函数不相等