分享

客户端脚本语言:JS的运行机制

 scxingm 2015-11-24

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。






代码块: JS中的代码块是指由<script>标签分割的代码段。JS是按照代码块来进行编译和执行的,代码块间相互独立(即就算代码块1出错,但不影响代码块2的加载和执行),但变量和方法共享。


案例:2个代码块


<script type='text/javascript'>

console.log('这是代码块一');

</script>


<script type='text/javascript'>

console.log ('这是代码块二');

</script>


HTML页面中JS的加载原理: 在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面。同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它,在这个过程中,页面的渲染和用户互交完全被阻塞。由于现代浏览器都允许并行下载JS文件,因此<script>标签在下载外部资源时不会阻塞其他的<script>标签。遗憾的是JS下载过程仍然会阻塞其他资源的下载。


JavaScript的单线程:


JS语言的一大特点就是单线程,也就是说,同一个时间只能做一件事情。之所以是单线程,是因为与它的用途有关,作为浏览器脚本语言,JS的主要用途是与用户互动以及操作DOM。这决定了它只能是单线程,否则会带来复杂的同步问题。为了利用多核CPU的计算功能,HTML5提出了web worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不能操作DOM,所以这个新标准并没有改变JS单线程的本质。


JavaScript的任务列队:


JS任务可以分为两种:一种是同步任务,另一种是异步任务。注意,只有主线程空了,才会去读取'任务队列',这就是JS的运行机制,这个过程会不断重复。


同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕了,才会执行后一个任务。


异步任务:在主线程之外,还存在一个“任务列队”,异步任务就是不进入主线程,而是进入“任务列队”的任务,只有“任务列队”通知主线程,某个异步任务可以执行了并且同步任务执行完毕,该任务才会进入主线程执行。


Javascript的事件和回调函数:


'任务列队'是一个事件的列队,IO设备完成一项任务,就在'任务队列'中添加一个事件,表示相关的异步任务可以进入'执行栈'了。主线程读取'任务队列',就是读取里面有哪些事件。'任务队列'中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入'任务队列',等待主线程读取。


所谓'回调函数',就是那些会被主线程挂起的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。'任务队列'是个先进先出的数据结构,排在前面的事件,优先被主线程读取。


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


定时器:


除了放置异步任务的事件,'任务队列'还可以放置定时事件,即指定某些代码在多少时间之后执行。定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeou()方法:


setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数


console.log(1)

setTimeout(function (){

console.log(2)

}, 1000);

console.log(3)


上面代码的执行结果是1=>3=>2,因为setTimeout()将第二行推迟到1000毫秒之后执行


如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后立即执行


setTimeout(function (){

console.log(2)

}, 0);

console.log(3)


上面代码的执行结果是3=>2,因为只有在执行完第二行以后,系统才会去执行'任务队列'中的回调函数。总之,setTimeout(fn, 0)的含义是,指定某个任务在主线程最早的空闲时间执行,也就是说尽可能早的执行。它在'任务队列'的尾部添加一个事件,因此要等到同步任务和'任务队列'现有的事情都处理完,才会得到执行。


需要注意的是,setTimeout()知识将事件插入了'任务队列',必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数,要是当前代码耗时很长,有可能要等很久,所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行。



内容来源:博客园

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多