一、call
1、语法:function.call(thisArg, arg1, arg2, ...) 可以看出call有两个作用,一是改变this指向,另一个传递参数,代码如下:
const obj = {
value: 1,
}
function fn(arg) {
console.log(this.value, arg); // 1, 2
}
fn.call(obj, 2);
2、实现思路 不使用显示绑定fn怎么拿到obj的value
const obj = {
value: 1,
fn: function() {
console.log(this.value); // 1
}
}
obj.fn();
这样this就指向了obj,根据这个思路可以封装一个方法,将传入的this转换成这样的方式,那么当前this的指向就是我们想要的结果。注意fn不能写成箭头函数,this会绑到window,所以实现步骤为:
- 将函数设置为传入对象的属性
- 执行该函数
- 删除该属性
// 给obj添加属性func
obj.func = fn;
// 执行函数
obj.func();
// 删除添加的属性
delete obj.func;
3、模拟实现call
Function.prototype.mycall = function (thisArg) {
thisArg.func = this;
thisArg.func();
delete thisArg.func;
}
const obj = {
value: 1,
}
function fn(arg) {
console.log(this.value, arg); // 1, undefined
}
fn.mycall(obj, 2);
上面已经改变了this指向,但是传参没有拿到,因为传参不确定,可以使用剩余参数。
Function.prototype.mycall = function (thisArg, ...args) {
thisArg.func = this;
thisArg.func(...args);
delete thisArg.func;
}
const obj = {
value: 1,
}
function fn(value1, value2) {
console.log(this.value, ...arguments) // 1 2 [1, 2] {a: 1}
}
fn.mycall(obj, 2, [1,2], { a: 1 })
但是还有一种情况,函数本身还有返回值
function fn(value1, value2) {
console.log(this.value, ...arguments) // 1, 2, [1,2], { a: 1 }
return {
c: 1
}
}
console.log(fn.mycall(obj, 2, [1,2], { a: 1 })) // undefined
解决方案:将执行的函数结果存下来,在return出来。所以方案如下:
Function.prototype.mycall = function(thisArg, ...args) {
// 在传入的对象上设置属性为待执行函数
thisArg.fn = this;
// 执行函数
const res = thisArg.fn(...args);
// 删除属性
delete thisArg.fn;
// 返回执行结果
return res;
}
最后,当this传null和undefined时,将是js执行环境的全局变量,浏览器中是window,node为global,以及设置一个原属性不重名的新属性,所以最终版代码如下:
Function.prototype.mycall = function(thisArg, ...args) {
thisArg = thisArg || window;
const key = Symbol('key')
thisArg[key] = this
var res = thisArg[key](...args)
delete thisArg[key]
return res
}
二、apply
1、语法func.apply(thisArg, [argsArray]) 与call相比第二个参数为数组,思路和call一样
Function.prototype.myApply = function (thisArg, args) {
thisArg = thisArg || window;
const key = Symbol('key')
thisArg[key] = this
const res = thisArg[key](...args)
delete thisArg[key]
return res
}
const obj = {
value: 1,
}
function fn() {
console.log(this.value); // 1
return [...arguments]
}
console.log(fn.myApply(obj, [1, 2])); // [1, 2]
三、bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法: function.bind(thisArg[, arg1[, arg2[, ...]]])
与前两个的区别,bind会创建一个新的函数,返回一个函数,并允许传参。
Function.prototype.myBind = function (thisArg, ...args) {
return (...reArgs) => {
return this.call(thisArg, ...args, ...reArgs)
}
}