JS全书:JavaScript Web前端开发指南
上QQ阅读APP看书,第一时间看更新

1.1 JavaScript简介

JavaScript是一种解释型的语言,通过解释执行。其解释器被称作“JavaScript引擎”,常见的JavaScript引擎有以下几种。

  • JavaScriptCore,用于Safari浏览器。
  • JaegerMonkey,用于Mozilla浏览器。
  • Chakra,用于IE、Edge浏览器。
  • V8,用于Chrome、Node.js浏览器。
  • Carakan,用于Opera浏览器。

JavaScript引擎按照ECMAScript标准定义一些规则,以此确定如何解析并执行JavaScript代码,尽管有一个标准来约束JavaScript引擎的实现方式,但各浏览器厂商也有可能不按照标准来实现,这也是为什么JavaScript会有浏览器兼容性问题的原因。

JavaScript是单线程(执行线程)的,即同一时刻仅有一处代码正在执行。JavaScript单线程的目的是为了避免多线程冲突,例如,多线程下同时操作同一个DOM节点就可能导致每次运行都会产生不同的结果,甚至抛出异常。

时代在不断前进,为了利用多核CPU的计算能力,HTML5提出了一个新的标准——Web Worker,该标准允许JavaScript创建多个线程,但被创建的子线程完全受主线程控制,并且子线程拥有一个独立的全局作用域,因此,子线程不能操作主线程上的DOM对象,以此来避免DOM操作冲突,而又达到充分利用多核CPU计算能力的目的,但实际上,这个新标准并没有改变JavaScript单线程的本质。

尽管JavaScript是单线程执行的,但浏览器并不是单线程执行的,浏览器的线程类型有JavaScript的执行线程、页面渲染线程、事件触发线程、http请求线程等。

JavaScript事件循环机制如下。

  • 函数调用堆栈。
  • 事件队列,即等待执行的事件处理程序队列。
  • 事件循环,待函数调用堆栈为空时,从最先进入事件队列中的消息开始处理,将消息从队列中移出并推至函数调用堆栈。

在这里,可以简单地把函数的调用堆栈为空理解为主线程代码执行完毕。

以事件触发线程为例,当创建一个事件时,事件循环机制会将事件回调函数放入事件队列中,当这个事件发生时,事件循环机制将其从事件队列中移出并推入函数调用堆栈中执行。

同理,即便setTimeout的延迟执行时间为0,其中的异步代码也会等到JavaScript主线程代码执行完毕时才执行。

上述代码中涉及两个函数——console.log和setTimeout。console.log函数会在控制台中输出指定值;setTimeout函数用于在指定的毫秒数后执行一段代码。了解了这两个函数的功能后,再来分析以上代码,其中,两个setTimeout创建的延迟执行代码没有立即执行,而是进入事件队列中,待主线程执行完console.log(1); console.log(3); console.log(5);之后,主线程中的代码已经执行完毕。这时,事件队列中的代码将会被推入主线程中执行,因此,程序先输出1、3、5,而后输出2、4。

鉴于此,setTimeout还可以用来解决onkeydown获取的value不正确的问题。

      <input type="text" onkeydown="console.log(this.value)">
      <input type="text" onkeydown="setTimeout(()=>{console.log(this.value)}, 0)">

因为setTimeout是异步的,需要等到主线程代码执行完毕,其中的代码才会执行,因此,直到输入值并在DOM上渲染完成时才会执行其中的代码,而此时获取的值就是我们的期望值了。