本文将介绍 this 关键字在函数、箭头函数上下文,以及修改this关键字的方法。
一、this 关键字调用方式
1、普通函数调用
function getThis() {
console.log(this);// this === window
}
当函数作为普通函数调用时,this指向在严格模式和非严格模式下有所不同。严格模式下,this指向undefined,非严格模式下,this指向window。
2、函数作为对象方法调用
const obj = {
name: 'Lee',
sayHi: function(){
console.log(this)// this === {name: 'Lee', sayHi: ƒ}
}
}
obj.sayHi()
当函数被作为对象的方法调用时,其中的 this 指向该对象本身。上述代码:sayHi函数作为obj的一个方法调用。所以this指向obj,而不是全局对象window。
3、构造函数调用
function Person(name, age){
this.name = name;
this.age = age;
console.log(this);// this === Person {name: 'Lee', age: 22}
}
const person = new Person('Lee', 22);
通过 new 关键字创建实例时,this 关键字会指向新创建的对象。
4、箭头函数调用
const obj = {
name: 'Lee',
greetting: function() {
const arrowFunc = () => {
console.log(this);// this === {name: 'Lee', greetting: ƒ}
};
return arrowFunc();
}
};
console.log(obj.greetting());
const obj = {
name: 'Lee',
sayHi: () => {
console.log(this);// this === window
}
};
obj.sayHi();
箭头函数的 this 绑定与常规函数不同,箭头函数没有自己的 this 值,而是捕获了封闭上下文的 this 值。所以上述代码中,箭头函数中的this引用的就是最近作用域中 this。
5、函数调用时使用call或apply
const obj = {
name: 'Lee'
}
function useCallOrApply(x, y){
console.log(x, y);
console.log(this);
}
useCallOrApply.call(obj, 1, 2);// this === obj 1、2 为参数
useCallOrApply.apply(obj, [1, 2]);// this === obj [1, 2] 为参数
通过使用函数的 call 或 apply 方法,可以显式地指定函数执行时的上下文,即 this 的值。在上述代码中,useCallOrApply.call(obj, 1, 2) 和 useCallOrApply.apply(obj, [1, 2]) 中的 this 都被绑定到了 obj 对象.
二、自定义修改this关键字函数
1、手写call
call():在使⽤⼀个指定的 this 值和若⼲个指定的参数值的前提下调⽤某个函数或⽅法。 call传入的参数不固定,第一个参数代表函数体内this指向,从第二个参数开始往后,每个参数被一次传入函数。
call 函数代码实现如下:
Function.prototype.myCall = function(context) {
// 判断调用对象是否为函数
if(typeof this !== "function") throw new Error('调用对象应为函数!')
// 判断context上下文是否存在;否的话,context设置为window
if(typeof context === 'undefined' || context === null) context = window;
// 获取参数
let args = [...arguments].slice(1);
let result = null;
// 将函数作为上下文对象的一个属性。
context.fn = this;
// 调用函数
result = context.fn(...args);
// 删除属性
delete context.fn;
return result;
}
const obj = {
name: 'Lee',
isWorking: true,
}
function getUserWork(x, y) {
console.log(x, y);
console.log(this);
}
getUserWork.myCall(obj, 2, 4);// this === obj
getUserWork.myCall();// this === window
getUserWork.myCall(null, 2, 4);// this === window
2、手写apply
apply 的实现跟 call 类似,只是⼊参不⼀样,apply为数组;
apply 函数代码实现如下:
Function.prototype.myApply = function(context) {
// 判断调用对象是否为函数
if(typeof this !== "function") throw new Error('调用对象应为函数!')
// 判断context上下文是否存在;否的话,context设置为window
if(typeof context === 'undefined' || context === null) context = window;
// 获取参数
let result = null;
context.fn = this;
// 判断是否传参
result = arguments[1] ? context.fn(...arguments[1]) : context.fn();
// 删除属性
delete context.fn;
return result;
}
const obj = {
name: 'Lee',
isWorking: true,
}
function getUserWork(x, y) {
console.log(x, y);
console.log(this);
}
getUserWork.myApply(obj, [2, 4]);// this === obj
getUserWork.myApply();// this === window
getUserWork.myApply(null, [2, 4]);// this === window
3、手写bind
bind() ⽅法会创建⼀个新函数。当这个新函数被调⽤时,bind() 的第⼀个参数将作为它运⾏时的 this,之后的⼀序列参数将会在传递的实参前传⼊作为它的参数。
bind 函数代码实现如下:
Function.prototype.myBind = function(context) {
// 判断调用对象是否为函数
if(typeof this !== "function") throw new Error('调用对象应为函数!')
// 判断context上下文是否存在;否的话,context设置为window
if(typeof context === 'undefined' || context === null) context = window;
const fn = this;
// 调用apply来绑定函数调用,返回一个函数
return function(...args) {
return fn.apply(context, args);
}
}
const obj = {
name: 'Lee',
isWorking: true,
}
function getUserWork(x, y) {
console.log(x, y);
console.log(this);
}
const mybind1 = getUserWork.myBind(obj, [2, 4]);// this === obj
mybind1();
const mybind2 = getUserWork.myBind();// this === window
mybind2();
const mybind3 = getUserWork.myBind(null, 2, 4);// this === window
mybind3();
const mybind4 = getUserWork.myBind(obj);// this === obj
mybind4();