探索JavaScript:你真的了解它吗?

发表时间: 2019-07-08 08:08

什么是JavaScript?

为前端开发,你是否问过自己或者思考过什么是JavaScript吗?JavaScript有什么特点?如果让你让一句话高度介绍,你会怎么说?小编认为,在你想深入一门语言,必须要清楚理解这们语言有什么特点和其中的运行机制,这是学好一门语言的基础。

JavaScript是一个单线程、非阻塞、异步、解释性语言。

单线程是个什么鬼?

计算机基础知识的同学可以忽略这部分内容,首先我们来一起了解下计算机基础知识:线程和进程

打个比方,我们去超市购物,结账的时候会有多个收银窗口,这样的好处就是在同一时间完成更多交易处理。这就是计算机常说的多并发概念,操作系统是多并发执行任务,因为它同时运行多个进程。进程是执行环境或正在运行的应用程序的实例。例如,你可能一边浏览着网页、一边打开编辑器写着代码、一边开着微信聊着天,这都要归功于计算机能同时运行多个应用进程。

应用程序也是可以处理多并发的,主要是靠线程实现的。在其他高级语言,如果你熟悉JAVA就会很容易理解,JAVA可以轻松创建多个线程处理并发问题,比如同时处理发出HTTP请求,查询数据库或打开文件。但是JavaScript是单线程的运行环境,它有且只有一个调用栈,它每次只能做一件事,程序每次只能运行一段代码,这就是单线程。单线程更通俗的解释就是——所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

什么是调用栈?(call stack)——代码在运行过程中,会有一个叫做调用栈(call stack)的概念。调用栈是一种栈结构,它用来存储计算机程序执行时候其活跃子程序的信息。(比如什么函数正在执行,什么函数正在被这个函数调用等等信息)。调用栈是解析器的一种机制。

首先一起看下一段简单的代码,我们先了解下Javascript是如何运行的?

首先按照函数调用的顺序进栈,然后函数执行完了,将值传给下个函数,出栈,最后将结果输出。

阻塞是个什么东东?

什么是阻塞,没有严格的定义什么是阻塞。仅仅是指代码运行得很慢,比如说console.log不慢,遍历从1到10亿次很慢,你可以可以暂且想想下,如果你用浏览器打开一个网页,一个网页肯定会涉及到各种数据的请求,图片、接口数据、CSS文件之类,如果JavaScript不想办法解决优化单线程加载数据问题的话,我们浏览网页的体验可能是这样的:先出来一个空白页面,过了一会一张图片出来了,然后在出来一段文字,过了一会接口数据读取完了,显示了一段文字,就这样网页像挤牙膏似的一点点的显示出来,这样的浏览体验你还会看网页吗?。因此更白话点在栈里表现很慢的东西都叫阻塞。

就像下图的代码,小编写了一个自己调用自己的函数,让函数进入无线循环的过程,造成了调用栈里需要执行大量的函数,模拟了一个阻塞,浏览器实在无法承受调用之痛,报错了!

秘密武器——非阻塞、异步回调

由于JavaScript要解决这个问题,必须要突破单线程的瓶颈,“异步回调”就成为JavaScript的秘密武器,完美的解决了此问题。异步回调让其拥有了“多线程”的能力,其实并不其然,异步回调是怎么解决并发问题,阻塞问题,不知道各位思考过背后的运行进制吗?

维基百科是这么解释回调函数的:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

通俗点回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。有点不好理解,小编在说的直白些就是——B函数被作为参数传递到A函数里,在A函数执行完后再执行B。

了解完异步回调的概念后,我们来看看JavaScript是如何运行的?首先看看下面一张图

在介绍这张图时,我们先了解下什么是任务队列——所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

通过下图将异步函数的执行步骤可视化,让我们更加容易理解上诉文字内容。

今天的介绍就到这里,想必大家都十分清楚JavaScript的特点了,我们一起了解什么是单线程、进程、阻塞、调用堆栈、异步回调、任务队列、事件循环等概念,同时又清楚了JavaScript的运行机制,今天的内容大家都学会了吗。

注本文内容参考视频 Philip Roberts的演讲《Help, I'm stuck in an event-loop》
https://www.youtube.com/watch?v=8aGhZQkoFbQ

更多精彩内容,请微信关注“前端达人”公众号