最近看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,没啥好说的。

至于 15this.val 指向的是上一层对象 b 中的 val3 乘以 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的指向,不必硬背

分类: JavaScript 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录