js改变this指向的方法有三种,分别为call、apply和bind方法,我们来手动实现一下它们。 首先定义一个基础对象和函数,用来演示上述方法。
let obj = {
name: 'obj'
}
let fn = function (method, a, b) {
console.log(`使用了${method}--名字是${this.name}---结果是${a + b}`)
}
call方法的使用
call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。 使用如下
fn.call(obj, 'call', 1, 2)// 使用了call--名字是obj---结果是3
手写myCall方法
- js的this指向调用者,谁调用函数,this指向谁
- 如果构造出obj.fn,那么fn的this就指向了obj 基于以上分析,我们来实现一下myCall方法
Function.prototype.myCall = function (target = window, ...args) {
let self = this//这里的this即fn
target.fn = self; //相当于在obj对象上增加了一个值为fn的属性
target.fn(...args)//即obj调用了fn, fn指向了obj
delete target['fn']//删除该属性
return target
}
执行下myCall方法,结果如下
fn.myCall(obj, 'myCall', 1, 2); // 使用了myCall--名字是obj---结果是3
apply的使用
apply()方法调用一个具有给定this值的函数,以及一个数组(或一个类数组对象)的形式提供的参数。使用如下
fn.apply(obj, ['apply', 1, 2]); // 使用了apply--名字是obj---结果是3
手写myApply方法
myApply 和myCall只是传参不同,其余一样。
Function.prototype.myApply = function (target = window, args) {
let self = this//指向fn
target.fn = self;
target.fn(...args)//target调用了fn, fn指向了target
delete target['fn']//删除该属性
return target
}
执行myApply方法,结果如下
fn.myApply(obj, ['myApply', 1, 2]); //使用了myApply--名字是obj---结果是3
bind方法的使用
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用使用。
let bind1 = fn.bind(obj, 'bind1', 1, 2); // 一次性传参
let bind2 = fn.bind(obj, 'bind2', 1);
bind1(); //使用了bind1--名字是obj---结果是3
bind2(2); // 使用了bind2--名字是obj---结果是3
手写myBind
- bind函数需要返回一个函数,并且涉及到参数合并fn.bind(obj, 'bind2', 1)
- 返回的函数涉及到直接调用和new的方式调用 基于以上分析思路,我们来实现一下myBind
Function.prototype.myBind = function (target = window, ...args) {
let self = this; //指向fn
let resFn = function (...innerArgs) {//返回一个函数
// 判断是否是构造函数,即new resFn() 执行的;
//如果是构造函数,直接指向new的对象,即resFn;
//如果直接调用,直接使用apply改变this指向
let isConstuctor = this instanceof resFn;
let context = isConstuctor ? this : target;
return self.apply(context, args.concat(innerArgs))
}
// 如果绑定的是构造函数,那么需要继承构造函数原型属性和方法:保证原函数的原型对象上的属性不丢失
// 实现继承的方式: 使用Object.create
resFn.prototype = Object.create(this.prototype);
return resFn;
}
执行结果如下
let myBind1 = fn.myBind(obj, 'myBind1', 1, 2)//一次性传参
let myBind2 = fn.bind(obj, 'myBind2', 1)
myBind1(); //使用了myBind1--名字是obj---结果是3
myBind2(2); //使用了myBind2--名字是obj---结果是3