深入理解 JavaScript 中的 this
在 JavaScript 中,this 是一个非常重要的关键字,它指向函数运行时所在的执行上下文。理解 this 的行为对于编写健壮的 JavaScript 代码至关重要。本文将详细探讨 this 的工作原理,并介绍几种常见的 this 调用方式及其应用场景。
this 的基本概念
this 是一个指针,指向函数被调用时的执行上下文。这个上下文取决于函数是如何被调用的。this 的值不是在函数定义时确定的,而是在函数执行时根据调用方式动态决定的。
要完全理解this 首先要清楚这些感念调用栈、执行上下文、作用域 和 作用域链
调用栈(Call Stack)是程序执行过程中,用于追踪函数调用顺序和管理函数执行上下文的一种数据结构。它以“后进先出”(LIFO, Last In First Out)的方式工作,即最后被调用的函数最先完成并退出栈。
调用栈的执行过程
示例代码
function first() {
console.log('In first');
second();
console.log('Back to first');
}
function second() {
console.log('In second');
third();
console.log('Back to second');
}
function third() {
console.log('In third');
}
first();
执行流程分析
-
first()调用:first被压入调用栈。- 执行
console.log('In first')。
-
second()调用:second被压入调用栈。- 执行
console.log('In second')。
-
third()调用:third被压入调用栈。- 执行
console.log('In third')。 third执行完毕,从调用栈弹出。
-
返回
second:- 执行
console.log('Back to second')。 second执行完毕,从调用栈弹出。
- 执行
-
返回
first:- 执行
console.log('Back to first')。 first执行完毕,从调用栈弹出。
- 执行
执行上下文 和作用域 这个在我之前的文章中详细讲解过如何你还不太了解可以点这个传送门 执行上下文和作用域
作用域链是 JavaScript 中用来管理变量访问的机制,它决定了在当前作用域中如何查找变量。作用域链的本质是将作用域按照嵌套关系连接起来,形成一条链条。JavaScript 引擎会根据这条链逐层查找变量。 简单来说就是作用域链是变量查找的规则,引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没有找到,查找过程都会停止。
欧克 理解完这些感念,让我们正式揭开this的面纱
this实际上是一个指向函数调用对象的指针。this的值在函数执行的一刹那由调用方式决定。
this的调用方法总的可以分为四种
1. 函数调用
var x=2;
var obj={
x:1,
foo:function(){
console.log(this.x);
}
}
obj.foo();// 1
当this作为函数被调用时this值不固定,只有在函数执行的一刹那被调用方式(谁)决定
2.普通函数调用
var x=2;
var obj={
x:1,
foo:function(){
console.log(this.x);
}
}
// 函数体
var foo=obj.foo;
foo();// 2
当普通函数调用this时this指向的是全局对象,所以this指向的全局变量x=2,这是在非严格模式下,当在严格模式下,普通函数调用this时this为undefined
3.构造函数调用
function Person(name) {
this.name = name;
}
const person = new Person("Alice");
console.log(person.name); // Alice
在使用 new 调用构造函数时,this 指向新创建的对象。
4.指定this 调用方式
var name="李荣浩"
var a={
naem:"薛之谦",
func1:function(){
console.log(this.name);
},
func2:function(){
setTimeout(function(){
this.func1();
}.call(a),1000)
}
}
a.func2();//薛之谦
按我们之前讲的属性func2上的方法function(){}是一个普通函数,它的this应该指向的是全局变量name=“李荣浩”,但为什么输出的是对象a里面的变量呢?
其实这里使用了call,当使用call调用函数时可以显示指定this的指向,这里就是让this指向了a的词法作用域,所以输出的是“薛之谦”。 值得注意的是使用call时与前几种this的指向不同的是,在使用call时this的指向是在编译阶段就完成了而其他几种是在函数被调用时才确定的
那还有没有其他方法也可以实现这个效果呢
var=“李”;
var a={
name:"肖",
func1:function(){
console.log(this.name);
},
func2:function(){
setTimeout(()=>{
this.func1()
},1000)
},
}
a.func2()
这里的输出结果为”肖“,这是怎么实现的呢?
** 其实在箭头函数中没有this 箭头函数中的this由外层作用域决定,它的this指向是在箭头函数被定义的时候就确定了,就和使用call一样在函数被调用之前就已经确定好了。
让我们在来看一种方法
func2:function(){
let that=this;
setTimeout(function()
that.func1()
},1000)
},
我们这里使用了this 丢失: 如果我们在function函数这里使用this它是指向全局的,但是我们不需要 我们需要的是a所以我们可以用一个变量来保存当前上下文的this
总结:如何判断 this 的指向?
-
箭头函数:
this由定义时外层作用域决定。 -
普通函数:
- 在全局调用时,
this指向全局对象(严格模式下为undefined)。 - 作为对象的方法调用时,
this指向调用该方法的对象。
- 在全局调用时,
-
new调用:this指向新创建的对象。 -
call显式设置this。