闲言碎语我要讲
-
call,apply,bind
都是改变this
指向的方法 -
用的时候不知道该用哪一个,在此梳理一下,加深印象
call
call()
函数接受多个参数,第一个参数为this
指向对象,后面参数为传给调用函数的参数,返回值是使用指定的this
值和参数调用函数后的结果。
scss
call(thisArg, arg1, arg2, /* ..., */ argN)
- 如果省略第一个
thisArg
参数,则默认为undefined
。在非严格模式下,this
值将被替换为globalThis
(类似于全局对象)。
js
var animal = '狗'
var sleepDuration = "6 到 8 小时"
function greet() {
console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
}
const obj = {
animal: "猫",
sleepDuration: "12 到 16 小时",
};
greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间
greet.call(); // 狗 的睡眠时间一般在 6 到 8 小时 之间
### 在greet.call(obj)执行时,greet()函数内的this指向了obj这个对象
### 就像是obj添加了一个属性greet,值就是greet函数,obj.greet = greet
### greet.call(obj) 就变成了 obj.greet()
- 手写
call
js
Function.prototype.zyCall = function(thisArg,...args){
const fn = this
// 使用时 fn.zyCall(obj),this始终指向调用它的对象,this == fn
thisArg = (thisArg !== null && thisArg !==undefined) ? Object(thisArg) : window
thisArg.fn = fn
const res = thisArg.fn(...args)
delete thisArg.fn
return res
}
apply
-
这个函数与
call()
几乎完全相同,只是函数参数在call()
中逐个作为列表传递,而在apply()
中它们会组合在一个类数组对象中,用于指定调用func
时的参数,或者如果不需要向函数提供参数,则为null
或undefined
-
手写
apply
js
Function.prototype.zyApply = function(thisArg,argArray){
const fn = this
// 使用时 fn.zyCall(obj),this始终指向调用它的对象,this == fn
thisArg = (thisArg !== null && thisArg !==undefined) ? Object(thisArg) : window
thisArg.fn = fn
const res = thisArg.fn(...argArray)
delete thisArg.fn
return res
}
bind
bind()
方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其this
关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。(这里有两个参数,一个是调用bind()时的参数arg1,一个是调用新函数时的参数arg2,新函数的实际参数是arg1和arg2两个)
js
bind(thisArg, arg1, arg2, /* ..., */ argN)
// 顶级的"this"绑定到"globalThis"。
var x = 9;
const module = {
x: 81,
getX() {
return this.x;
},
};
// "getX"的"this"参数绑定到"module"。
console.log(module.getX()); // 81
const retrieveX = module.getX;
// "retrieveX"的"this"参数在非严格模式下绑定到"globalThis"。
console.log(retrieveX()); // 9
// 创建一个新函数"boundGetX",并将"this"参数绑定到"module"。
const boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81
-
bind()
函数创建一个新的绑定函数(bound function) 。调用绑定函数通常会执行其所包装的函数,也称为目标函数(target function) 。绑定函数将绑定时传入的参数(包括this
的值和前几个参数)提前存储为其内部状态。而不是在实际调用时传入。通常情况下,你可以将const boundFn = fn.bind(thisArg, arg1, arg2)
和const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs)
构建的绑定函数的调用效果视为等效(但就构建boundFn
的过程而言,不是二者等效的)。 -
绑定函数可以通过调用
boundFn.bind(thisArg, /* more args */)
进一步进行绑定,从而创建另一个绑定函数boundFn2
。新绑定的thisArg
值会被忽略,因为boundFn2
的目标函数是boundFn
,而boundFn
已经有一个绑定的this
值了。当调用boundFn2
时,它会调用boundFn
,而boundFn
又会调用fn
。fn
最终接收到的参数按顺序为:boundFn
绑定的参数、boundFn2
绑定的参数,以及boundFn2
接收到的参数。
js
"use strict"; // 防止 `this` 被封装到到包装对象中
function log(...args) {
console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6
- 手写
bind
js
Function.proptype.zyBind = function (thisArg, ...argArray) {
//1.获取真实需要调用的函数
const fn = this
//2. 绑定this
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
function proxyFn(...args) {
//3.将函数放到thisArg中进行调用
thisArg.fn = fn
//特殊:对两个传入的参数进行合并
const finalArgs = [...argArray, ...args]
const res = thisArg.fn(...finalArgs)
delete thisArg.fn
//4.返回结果
return res
}
//返回要调用的函数本身,这里是添加过this绑定值的函数
return proxyFn
}
总结
call, apply
要稍微简单一点,bind
难一点,讲得不是很清楚,具体戳这