由setTimeout的this讲起

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_26598303/article/details/52668683

在最近一次面试中,技术官问了setTimeout中this的指向,看了无数遍的this相关的东西,没想到竟然在这个问题上栽了跟头……都怪我经验太少看书太少哎,所以来认真研究了一番这东西。
虽然setTimeout()平时也有用过,但理解得并不深。JavaScript高级程序设计有说:超时调用的代码都是在全局作用域中执行的,因此函数中的this的值在非严格模式下指向window对象,在严格模式下是undefined。我认真地测试过一遍,思考过程如下:
这里写图片描述
所以结论是,setTimeout()中,如果是setTimeout(this)这样的形式,那么this要看上下文,默认是window;如果setTimeout里面的函数中的this的话,都统一指向window。
那么由此想到一个问题,如果像上面的代码,我想拿到this.name等于“yujiayu”要怎么做呢?因为上面的代码setTimeout里面的函数中的this指向window,那当然this.name肯定是undefined了。其实到这里答案就非常简单了,可以看到第14行代码的this是指向test1对象的,那只要我们保存一下这个变量,然后使用闭包就能获取到它了,因为闭包可以保存自身当前作用域上的变量(当然这肯定是耗费内存的)。代码:

function test1() {
   var _this = this; 
    this.name = "yujiayu";
    this.sayName = function() {
          console.log(this.name);
    }
    setTimeout(function () {
      // 由于闭包的原因保存了_this为test1对象,所以sayName()输出“yujiayu”
        _this.sayName();     
        },0);
    }
new test1();

至此setTimeout中this的问题就差不多了,然而引申出另外一个问题,就是“为什么常用setTimeout模拟setInterval,而不是直接用setInterval”
面试官谈到js的执行流:“js是单线程的,setTimeout会将函数放在等待序列中,到达设置的时间节点才会触发执行。但如果当前的线程
处于占用状态,setTimeout设置的函数并不会按时触发,而是会等到线程空闲了才被触发执行。”
按照我的理解,其实js的执行流应该跟我之前看的”Event Loop”是一个意思,这个图特别好说明这个问题:
这里写图片描述
如果按照上图EventLoop的说法来理解,就是,定时器会在指定的时间里,将要执行的东西推进回调队列里,但不保证这个时间点去执行这项任务。JS高级程序设计也有说这个事情(惭愧,这本书后面的内容我还没看到)“如果主程序跟定时器代码花的时间差不多,就会同时出现跳过间隔且连续运行定时器代码的情况”,于是需要用setTimeout去模拟setInterval:

setTimeout(function () {
    setTimeout(arguments.callee,500)
},500);

在测试过程中还发现了一个有趣现象:
这里写图片描述

原来加引号的console.log使用的是全局作用域,不加是使用自己作用域,而且看到加了引号的那个,来源是“VM1737”,它写的时候是排在第一,但输出出来它却排到第二……这个有点不科学,相同时间进入队列,当然是先进先出的原则,这个就不知道怎样解释了。这个”VMxxxx”的东西以前也不知道是什么东西,猜测是开发者工具自带的的东西,后来在stackoverflow发现有人说:[VM] (scriptId) has no special meaning. It’s a dummy name to help us to distinguish code which are not directly tied to a file name, such as code created using eval and friends.

展开阅读全文

没有更多推荐了,返回首页