深入探究:一篇文章搞懂JS this指向
引言
大家好,欢迎来到本篇博客!今天,我们将一起探讨JavaScript中一个常常让人感到困惑的话题——this指向。无论你是初学者还是有一定经验的开发者,对于this的概念理解都至关重要。让我们一起来揭开this的神秘面纱吧!
为什么要有this?
- 为了让对象中的函数有能力访问对象自己的属性。
以以下简单代码为例:

如果我们不使用this对象中的函数将无法访问对象自己的属性。加上this则可以访问。

- this可以显著的提升代码质量,减少上下文参数的传递。
第一部分代码是不使用this的传参模式,而第二部分使用this大大减少了参数的传递,提高了函数的灵活性和可复用性,使得这些函数在不同上下文中都能正常运行。如果第二部分代码暂时看不懂也没关系,等你看完这篇文章就可以一秒get。
function identify(context) {
return context.myname.toUpperCase();
}
function speak(context) {
var greeting = "Hello I am " + identify(context);
console.log(greeting);
}
var me = {
myname: 'xx'
}
speak(me);
function identify() {
return this.myname.toUpperCase();
}
function speak() {
var greeting = "Hello I am " + identify.call(this);
console.log(greeting);
}
var me = {
myname: 'xx'
}
speak.call(me);
this的指向规则
this绑定优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
默认绑定
当一个函数独立调用,不带任何修饰符的时候,this一定指向window。
在浏览器中通常是
window
对象,在Node.js环境中是global
空对象
再来一个简单的例子:
function foo() {
console.log(this); // window
}
function bar() {
foo()
}
function baz() {
bar()
}
baz()
因为this在foo中,而foo在调用时不带任何修饰符,所以this指向window。
隐式绑定
当函数作为对象的方法被调用时,this会指向调用该方法的对象。
const person = {
name: 'Alice',
greet: function() {
console.log(this.name);
}
};
person.greet(); // Alice
再来一个例子:

看上去像是obj调用了b函数,但是实际上this在foo内独立调用,所以仍然使用默认绑定规则,指向window
隐式丢失
隐式丢失是指函数在使用隐式绑定规则时,丢失了原本的预期的上下文,导致this
指向不再符合预期的情况。
这种情况通常发生在以下情形下:
- 将函数从原始对象中分离出来:
const obj = {
name: 'Alice',
greet() {
console.log(this); // Window
}
};
const greet = obj.greet; // 隐式丢失
greet(); // 在这里,隐式绑定丢失,this不再指向obj
- 将函数作为参数传递并在其他上下文中调用:
const obj1 = {
name: 'Alice',
greet() {
console.log(`Hello, ${this.name}`);
}
};
const obj2 = {
name: 'Bob'
};
obj1.greet(); // Hello, Alice
setTimeout(obj1.greet, 1000);
// 在这里,隐式丢失
当你传递一个函数给
setTimeout
时,它在指定的时间间隔后执行该函数。这个函数会在全局上下文中执行,而不是在定义时的上下文(例如对象中),因此会导致this
的指向改变。在这些情况下,
this
的上下文丢失了,可能会导致意外的行为或错误。为避免隐式丢失,可以使用箭头函数、bind
方法、call
、apply
来明确指定this
的上下文,或者在传递函数时保持上下文一致。
显示绑定
通过call
、apply
或bind
方法,我们可以显式地指定函数内部this的指向。
1. bind 方法:
- bind 方法创建一个新的函数,该函数在调用时将指定的
this
值绑定到传递给bind
的值,但并不立即调用该函数。 - 语法:
function.bind(thisArg, arg1, arg2, ...)
- bind 可以用于固定函数的
this
值,并传递初始参数。
function greet(name) {
console.log(`Hello, ${name}! I'm ${this.job}.`);
}
let person = {
job: 'engineer'
};
let greetPerson = greet.bind(person, 'Alice');
greetPerson(); // 输出:Hello, Alice! I'm engineer.
// bind返回函数调用时也可以传参,优先识别bind中的参数
//greetPerson('Tom');
- bind 多次指向 只识别第一次指向
var obj = {
a: 1
}
var obj1 = {
a: 2
}
function foo() {
console.log(this.a); // 输出 1
}
let res = foo.bind(obj).bind(obj1);
res()
- bind 优先级高于 call 和 apply。通过bind指定的this,call和apply无法再次更改。
var obj = {
a: 1
}
var obj1 = {
a: 2
}
function foo() {
console.log(this.a);
}
let res = foo.bind(obj); // 输出 1
res.apply(obj1);
2. call 方法:
- call 方法允许你在调用函数时指定函数内部的
this
值,以及以参数列表的形式传递参数。 - call 方法立即调用相应的函数。
- 语法:
function.call(thisArg, arg1, arg2, ...)
function greet(name) {
console.log(`Hello, ${name}! I'm ${this.job}.`);
}
let person = {
job: 'teacher'
};
greet.call(person, 'Bob'); // 输出:Hello, Bob! I'm teacher.
3. apply 方法:
- apply 方法与 call 类似,不同之处在于它接收一个数组或类数组对象作为参数。
- 语法:
function.apply(thisArg, [argArray])
function greet(name) {
console.log(`Hello, ${name}! I'm ${this.job}.`);
}
let person = {
job: 'doctor'
};
greet.apply(person, ['Charlie']); // 输出:Hello, Charlie! I'm doctor.
4. 关键区别:
bind
返回一个新函数,不会立即调用,允许延迟执行;而call
和apply
立即调用相应的函数。call
和apply
都允许你显式设置函数内部的this
值和传递参数,但apply
接收的参数是数组形式。bind
适用于创建具有永久this
绑定的新函数,而call
和apply
适用于在特定上下文中立即调用函数。
new绑定
当使用new
关键字创建实例时,this会绑定到它创建的实例对象上。
function Person(name) {
this.name = name;
}
const john = new Person('John');
console.log(john.name); // John
箭头函数中的this
- 箭头函数不存在this机制,所以箭头函数里的this会沿用外层最近一层非箭头函数的this。
const obj = {
func1: function() {
console.log(this); // obj
const func2 = () => {
console.log(this); // obj
};
func2();
}
};
obj.func1();
总结
经过这篇文章的探讨,相信大家对JavaScript中this的指向有了更清晰的认识。记住这些规则并在实际开发中多加练习,相信你很快就能够游刃有余地处理this的使用了! 希望这篇文章能够帮助到你,如果有任何疑问或想法,欢迎在评论区和我交流讨论。今天的内容就到这里了。如果你觉得这篇文章有帮助或有所启发,别忘了给我一个鼓励的赞哦!🚀🔍
转载自:https://juejin.cn/post/7366826090835853351