JavaScript基础——你真的了解JavaScript吗?
什么是JavaScript?
做为前端开发,你是否问过自己或者思考过什么是JavaScript吗?JavaScript有什么特点?小编认为,在你想深入一门语言,必须要清楚理解这门语言有什么特点和其中背后的运行机制,这是学好一门语言的基础。
JavaScript是一个单线程、非阻塞、异步、解释性语言。
单线程是个什么鬼?
首先我们来一起了解下计算机基础知识:线程和进程。
打个比方,我们去超市购物,结账的时候会有多个收银窗口,这样的好处就是在同一时间完成更多交易处理。这就是计算机理论常说的多并发,操作系统是多并发执行任务的,因为它同时运行多个进程。进程是执行环境或正在运行的应用程序的实例。例如,你可能一边浏览着网页、一边打开编辑器写着代码、一边开着微信聊着天,这都要归功于计算机能同时运行多个应用进程。
应用程序也是可以处理多并发的,主要是靠线程实现的。像高级语言JAVA就能很轻松的编写多线程应用处理程序。
但是JavaScript是单线程的运行环境,它有且只有一个调用栈,它每次只能做一件事,程序每次只能运行一段代码,这就是单线程。
单线程更通俗的解释就是——所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
什么是调用栈?(call stack)——代码在运行过程中,会有一个叫做调用栈(call stack)的概念。调用栈是一种栈结构,它用来存储计算机程序执行时候其活跃子程序的信息。(比如什么函数正在执行,什么函数正在被这个函数调用等等信息)。调用栈是解析器的一种机制。
阻塞又是个什么东东?
什么是阻塞,没有严格的定义什么是阻塞。仅仅是指代码运行得很慢,比如说console.log不慢,遍历从1到10亿次很慢。换句话说在栈里表现很慢的东西都叫阻塞。
就像下图的代码,小编写了一个自己调用自己的函数,让函数进入无线循环的过程,造成了调用栈里需要执行大量的函数,模拟了一个阻塞,浏览器实在无法承受调用之痛,报错了!
秘密武器—非阻塞、异步回调
由于JavaScript要解决这个问题,必须要突破单线程的瓶颈,“异步回调”就成为JavaScript的秘密武器,完美的解决了此问题。
异步回调让其拥有了“多线程”的能力,其实并不其然,异步回调是怎么解决并发问题,阻塞问题,不知道大家思考过背后的运行机制吗?
维基百科是这么解释回调函数的:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
通俗点回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。有点不好理解,小编在说的直白些就是——B函数被作为参数传递到A函数里,在A函数执行完后再执行B。
了解完异步回调的概念后,我们来看看JavaScript是如何运行的?首先我们一起来看看下面的图:
在介绍这张图前,我们先了解下什么是任务队列——所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。