bind、call、apply的使用
call:立即执行,可传入个参数,第一个参数为需绑定的this上下文,后面则为需传入的参数
call(this, arg1, arg2, ....)
apply:立即执行,传入两个参数,第一个参数为需绑定的this上下文,第二个参数为需传入的参数组apply(this, arrArgs)
bind: 不立即执行函数,传入参数同call方法bind(this, arg1, arg2, ...)
bind
使用场景: 不调用函数,又想改变函数内部this指向,比如定时器之类的问题
// 原理
Function.prototype.myBind = function(context = globalThis, ...args) {
const fn = this;
const newFunc = function() {
const newArgs = args.concat(...arguments)
if (this instanceof newFunc) {
// new 调用
fn.apply(this, newArgs);
} else {
// 普通函数调用
fn.apply(context, newArgs);
}
// fn.apply(this instanceOf newFunc ? this: context, newArgs)
}
newFunc.prototype = Object.create(fn.prototype)
return newFunc
}
// 常见应用场景
setTimeout(function() {
console.log("aa")
}.bind(this), 2000)
Tip: 多次绑定bind()是无效的。更深层次的原因,bind()的实现相当于使用函数在内部包了一个call / apply,第二次bind()相当于再包住第一次bind(),故第二次及以后的bind是无法生效的
第一次 bind 绑定的对象是固定的,也就是后面通过 bind 或者 call 再次绑定的时候,就无法修改这个 this 了
call
使用场景:主要用于继承
用来代替另一个对象调用一个方法(本身无该方法,才选择调用),call方法可以将一个函数的上下文从初始的对象转成obj指向的新对象,如果没有提供obj对象,那么Global对象被指做obj
function Product(name, price) {
this.name = name
this.price = price
}
function Food(name, price) {
// 调用Product的call方法并将当前作用域对象this传入替换掉Product内部的this作用域对象
Product.call(this, name, price)
this.category = 'food'
}
let tempFood = new Food('cheese', 5)
Function.prototype.myCall = function(context = globalThis, ...args) {
// 关键步骤,在context上调用方法,触发this绑定context,使用Symbol防止原有属性的覆盖
const key = Symbol("key");
// 绑定调用函数(.call之前的方法即this,前面提到过调用call方法会调用一遍自身,所以要存下来)
context[key] = this;
// 执行调用函数,记录拿取返回值
const res = context[key](...args);
// 销毁调用函数,以免作用域污染
delete context[key];
return res;
}
// 常见应用场景
// 继承
function A(name, sex) {
this.name = name;
this.sex = sex;
}
function B() {
A.myCall(this, "jack", "nv")
this.age = 12
}
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B
let b = new B();
console.log(b.name, b.sex)
// 验证对象类型
Object.prototype.toString.call(obj)
// 类数组对象使用数组方法
Array.prototype.slice.call(document.getElementsByTagName("*"))
apply
使用场景:经常跟数组有关系,比如借助Math内置对象实现数组最大最小值问题
Function.prototype.myApply = function(context = globalThis, ...args) {
const key = Symbol("key")
context[key] = this;
let res;
if (args[0]) {
res = context[key](...args[0])
} else {
res = context[key]()
}
delete context[key];
return res;
}
// 常见应用场景
// 1. 获取数组中的最大/小值
let arr = [1, 2, 3, 4]
let res = Math.Max.apply(null, arr)
console.log(res) // 4
// 2. 数组之间拼接
var arr1 = [12, "foo", {name: "Joe"}, -2548];
var arr2 = ["Doe", 555, 100];
Array.prototype.push.apply(arr1, arr2)
/* arr1 = [12, "foo", {name: "Joe"}, -2548, "Doe", 555, 100] */
...
当传入对象为数组时,若想使用call方法,可结合展开运算符
call(this, ...args)