引言
上一篇学习了在不同情况下this的指向,那这个指向能否人为修改呢,答案是肯定的,今天学习改变this指向的方法。
call
格式 fn.call(obj, params, ...); 调用call立即执行fn方法
let obj = {
name: '前端小菜鸟',
getName: function (age, sex) { console.log(`${this.name} 性别 ${sex} 从事前端工作 ${age} 年`)}
}
let newObj = {
name: '大菜鸟'
}
obj.getName(1, '男') // 前端小菜鸟 性别 男 从事前端工作 1 年
obj.getName.call(newObj, 3, '男'); // 大菜鸟 性别 男 从事前端工作 3 年
apply
格式 fn.apply(obj, [params1, params2, ...]) 用法和call一样,调用apply立即执行fn方法,区别在于apply接受的第二个参数是数组
let obj = {
name: '前端小菜鸟',
getName: function (age, sex) { console.log(`${this.name} 性别 ${sex} 从事前端工作 ${age} 年`)}
}
let newObj = {
name: '大菜鸟'
}
obj.getName(1, '男') // 前端小菜鸟 性别 男 从事前端工作 1 年
obj.getName.apply(newObj, [3, '男']); // 第二个参数是数组 大菜鸟 性别 男 从事前端工作 3 年
bind
格式 fn.bind(obj, params, ...) 接受参数和call一样 ,区别在于 bind返回一个新的函数,需要主动触发执行
let obj = {
name: '前端小菜鸟',
getName: function (age, sex) { console.log(`${this.name} 性别 ${sex} 从事前端工作 ${age} 年`)}
}
let newObj = {
name: '大菜鸟'
}
// 返回的是一个新的函数
console.log(obj.getName.bind(newObj, [3, '男'])); // [Function: bound getName]
obj.getName.bind(newObj, 3, '男')(); // 大菜鸟 性别 男 从事前端工作 3 年
特殊情况
非严格模式下 call、apply、bind如果不传参数时,或者第一个参数是null/undefined this都指向 Window
let obj = {
name: '前端小菜鸟',
getName: function () { console.log(this)}
}
obj.getName.call(); // Window
obj.getName.apply(); // Window
obj.getName.bind()(); // Window
obj.getName.call(null); // Window
obj.getName.apply(null); // Window
obj.getName.bind(null)(); // Window
obj.getName.call(undefined); // Window
obj.getName.apply(undefined); // Window
obj.getName.bind(undefined)(); // Window
严格模式下 第一个参数传谁就指向谁 包含null、undefined,如果不传指向undefined
"use strict"
let obj = {
name: '前端小菜鸟',
getName: function () { console.log(this)}
}
obj.getName.call(); // undefined
obj.getName.apply(); // undefined
obj.getName.bind()(); // undefined
obj.getName.call(null); // null
obj.getName.apply(null); // null
obj.getName.bind(null)(); // null
obj.getName.call(undefined); // undefined
obj.getName.apply(undefined); // undefined
obj.getName.bind(undefined)(); // undefined
that
开发过程中我们还可以用that来改变this指向
var name = '我是大菜鸟'
let obj = {
name: '前端小菜鸟',
getName: function () {
let that = this; // 用that保存this
setTimeout(function () {
console.log(this.name , that.name); // 定时器里this指向Window 我是大菜鸟 that指向当前对象 前端小菜鸟
}, 10);
}
}
obj.getName();
箭头函数
箭头函数无法用call、apply、bind改变this指向
var name = '大菜鸟'
let obj = {
name: "前端小菜鸟",
getName: (a,b) => {
console.log(this.name,a,b);
}
};
let fn = obj.getName;
fn(1,2); // 大菜鸟 1 2
fn.bind(obj, 3,4)(); // 大菜鸟 3 4
fn.call(obj,5,6);// 大菜鸟 5 6
fn.apply(obj,[1,2]);// 大菜鸟 7 8
call、apply、bind原理
call、apply、bind实际上都是function原型上的方法,所有实例都可以调用
call实现
let obj = {
name: '前端小菜鸟',
getName: function (age, sex) { console.log(this.name + "性别" + sex +'工作' + age + '年')}
}
let newObj = {
name: '大菜鸟'
}
function myCall() {
// 解构剩余参数
let [thisArg, ...args] = arguments;
thisArg = Object(thisArg) || window;
thisArg.fn = this; // 随便定义一个属性用来保存当前方法
let result = thisArg.fn(...args); // 执行方法得到结果
delete thisArg.fn; // 当前对象没有这个属性 所以要删除
return result; // 返回结果
}
Function.prototype._call = myCall; // Function原型上添加方法
obj.getName._call(newObj, 3, '男') // 大菜鸟性别男工作3年
// 极简版本
Function.prototype.Call = function (that, ...args) {
that.fn = this;
let res = that.fn(...args);
delete that.fn;
return res
}
apply实现
call都实现了 apply那不是手到擒来
let obj = {
name: '前端小菜鸟',
getName: function (age,sex) { console.log(`${this.name} 性别${sex}工作${age}年`)}
}
let newObj = {
name: '大菜鸟'
}
Function.prototype._apply = myApply;
function myApply() {
let [thisArg, args] = arguments; // 跟call不同的其实就在这里 获取参数的方式不同
thisArg = Object(thisArg) || window;
thisArg.fn = this;
let result = thisArg.fn(...args);
delete thisArg.fn;
return result;
}
obj.getName._apply(newObj,[3, '男']); // 大菜鸟 性别男工作3年
// 极简版本
Function.prototype.Apply = function (that, args) {
that.fn = this;
let res = that.fn(...args);
delete that.fn;
return res
}
bind实现
let obj = {
name: '前端小菜鸟',
getName: function (age,sex) { console.log(`${this.name} 性别${sex}工作${age}年`)}
}
let newObj = {
name: '大菜鸟'
}
Function.prototype._bind = function (that, ...args) {
let fn = this;
return function () {
return fn.call(that, ...args)
}
}
obj.getName._bind(newObj, 3, '男')();
总结
| call | apply | bind | |
|---|---|---|---|
| 调用后立即执行 | √ | √ | x |
| 第二个参数是数组 | x | √ | x |
| 返回值是一个函数 | x | x | √ |