js中this的指向问题
最近看js的代码,遇到this的问题,就看花了眼一脸懵逼。网上到处找资料,总算是看明白了。
话不多说,有如下代码:
window.val = 1;
var obj = {
val:2,
db1:function(){
val *= 2;
this.val *= 2;
console.log(val);
console.log(this.val);
}
};
obj.db1();
// 2 4
var func = obj.db1;
func();
// 这里可以看作 window.func()
// 8 8
结果:
obj.db1();
的结果想必并不难计算,打印结果是 2 4
func();
打印的是 8 8
解析:
在 obj.db1()
中:
db1
函数内表达式 val *= 2;
即 val = val * 2
,这个 val
没有使用 var
进行声明,则代表这是全局的 val
。在函数内部,如果没有用var
进行申明,则创建的变量是全局变量,而不是局部变量。
表达式中的第二个 val
,是一个自由变量。它在函数 db1
的作用域中没有进行过声明,找不到变量 val
,则会到上一层的作用域中寻找(注意我说的是上一层作用域,在ES5中,只有全局作用域和函数作用域 ,很明显 db1
函数并没有被另一个函数包裹,所以只能找到全局去),于是,找到了全局的 val
。
关于自由变量: 理解js中的自由变量以及作用域的进阶
JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升。优先级依次为:变量声明 函数声明 变量赋值。
关于变量提升,相关文章网上很多说明的,可自行查找。
在 func();
中:
结果估计会让有些小伙伴迷惑了,为什么是两个8?哪儿来的?
解析:
首先我们要知道,出现这个疑问,是因为没有搞清楚 this
的指向问题,迷惑的小伙伴正是没有搞清楚这个 this
到底指向了谁,才会计算错误的。
记住一点,this的指向只有在调用的时候才能确定,在函数创建的时候是决定不了this的指向的,简单的说,谁调用的就指向谁。
第一个结果,经过 obj.db1()
调用,db1()
函数是被 obj
对象调用的,因此 db1()
中的 this
指向的是 obj
对象。所以在这里 this.val
就是指的 obj
对象中的 val
,计算结果为 obj
中的 val
值为 4。
而 db1
函数中执行到 val*=2
时,因为 val
没有在 db1
函数中定义,那么它就会去全局 window
中找,因此找到了全局的 val
,即 window.val = 1
,所以计算后的结果为全局的 val
值为2,即 window.val=2
。所以结果是打印出 2 4
第二个结果,在上面的代码中,func()
可以看做的是 window.func()
,结果一样,两者等同。就和我们使用 alert
方法一样,其实 alert
方法也是 window
对象中的,可以这样调用 window.alert()
,但是通常我们使用时并没有打出这个 window
。因此 func()
调用后,this
的指向就是 window
了,那么 this.val
指向的就是 window.val=2
,因此 this.val*=2
就等同于 window.val*=2
,结果为 4。
然后 db1
函数中执行到 val*=2
时,因为 在函数内部,如果没有用var
进行申明,则创建的变量是全局变量,而不是局部变量 。很显然,全局中的 window.val
的值已经是4,再次 val*=2
结果就是8了,所以最后结果是打印出 8 8
参考文章: ES6箭头函数里的this
再看个例子:
window.val = 1;
var obj = {
val:2,
b:{
val:3,
db1:function(){
val *= 4;
console.log(this.val);
// undefined
this.val *= 5;
console.log(val);
console.log(this.val);
}
}
};
obj.b.db1();
// 3 4 15
var func = obj.b.db1;
func();
// 这里可以看作 window.func()
// 16 80 80
第一次调用 obj.b.db1();
的打印结果为 3 4 15
。
第一个 console.log(this.val);
打印结果为 3 ,可见 this.val
指向的是上一层对象 b
中的 val
。而不是 obj
中的 val
。更不是 window
中的 val
。
4 好理解,val *= 4
就是 val = val * 4
,因为 在函数内部,如果没有用var
进行申明,则创建的变量是全局变量,而不是局部变量 ,1*4
结果为 4,没啥好说的。
至于 15
, this.val
指向的是上一层对象 b
中的 val
,3
乘以 5,就是 15
。
第二次调用 func();
的打印结果为 16 80 80
。
第一个打印结果是 16 是不是又懵逼了?想不通?别慌,接下来就给你解释。
我们知道,经过第一次的调用,全局 val
的值经过计算后,现在是 4 了。
还记得吗? 在函数内部,如果没有用var
进行申明,则创建的变量是全局变量,而不是局部变量
val *= 4
是在函数中声明的,而且没有使用 var
进行声明,那么这个 val
就是全局变量 val
,即 window.val = window.val * 4 = 4 * 4 = 16
,就也就是说,经过这里的计算后,全局变量 val
的值就是 16
了。所以,其实 func();
调用时,它内部的对象 b
中的 db1
函数中的 this
,指向的是 window
。
是不是又懵了?不是刚还说指向的是上一层对象 b
中的 val
吗?这里为什么指向的又变成了全局 val
了?
记住,谁调用的就指向谁 。var func = obj.b.db1;
是赋值操作,并没有进行调用,func()
才进行了调用,而 func()
其实就是 window.func()
,是不是明白了?是 window
调用了它,所以当然指向的是全局的 val
啦。
接着说,全局 val
此时值为 16,再进行 this.val *= 5;
进行计算,因为是 window
调用的,this.val
就是全局 val
,没有 var
申明的 val
也是全局 val
,结果就是打印出两个 16 * 5 = 80
啦。
最终结论:
好吧,其实上面得出的结论有点小小的问题,需要补充下,不说代码了,太长了懒得写(主要是有现成的),先说结论:
- 情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
- 情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
- 情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象。
结论出处: 彻底理解js中this的指向,不必硬背
本文系作者 @枫雨 原创发布在枫林幻境站点。未经许可,禁止转载。
暂无评论数据