js进阶学习笔记(一) -- js部分原理

之前看了一些关于作用域的文章和书,可是都渐渐淡忘了,这里我在重新复习作用域之前,先去了解一下js引擎编译的大致过程,来帮助我加深对js的理解.

渲染引擎

浏览器的核心是两部分:渲染引擎和javascript解释器(引擎);不同的浏览器有不同的渲染引擎,他的主要作用是生成网页,通常分成四个阶段,因为图片看起来更加直观,所以把内容放在图片里.


渲染引擎工作.jpg

javascript虚拟机(引擎)

  • js是解释型语言,有一定的优缺点:
js引擎.jpg
  • 早期,浏览器内部对js的处理过程:
浏览器对JS的处理过程.png
  • 即时编译

为了提高运行速度,现代浏览器改为采用'即时编译'(just in time compiler,缩写JIT),即字节码只在运行时编译,用到哪行,就编译哪一行,并且把编译结果缓存,通常,一个程序被经常用到的,只是其中一小部分的代码,有了缓存的编译结果,整个程序的运行速度就会显著提升.

有的浏览器索性省略了字节码的翻译步骤,直接编译成机器码,比如CHROME浏览器的v8引擎.
  • 字节码不能直接运行,而是运行在一个虚拟机之上,一般也把虚拟机称为'javascript引擎'.因为js运行时未必有字节码,所以js虚拟机并不完全基于字节码,而是部分基于源码,即只要有可能,就通过JIT编译器直接把源码编译成机器码运行,省略字节码步骤,这一点与其他采用虚拟机的语言不尽相同,这样做的目的,是为了尽可能地优化代码,提高性能.

script标签的工作原理

正常的网页加载流程:

script.jpg
  • 为了避免发生阻塞效应,较好的做法是将script标签都放在底部,而不是头部.这样即使遇到脚本失去响应,网页主体的渲染也已经完成了,用户至少可以看到内容,而不是面对一张空白的页面.

  • 如果某些脚本代码非常重要,一定放在页面头部的话,最好将代码嵌入页面,而不是链接外部脚本文件,这样能缩短加载时间

  • 将脚本放在网页尾部加载还有一个好处,在DOM结构生成之前就调用DOM,js会报错,如果脚本都在网页尾部加载,就不存在这个问题,因为这时DOM肯定已经生成了.

defer属性

为了解决脚本文件下载阻塞网页渲染的问题,一个方法是加入defer属性

  • defer属性的作用是,告诉浏览器,等到DOM加载完成后,再执行指定脚本
defer.jpg
  • 有了defer属性,浏览器下载脚本文件的时候,不会阻塞页面渲染.下载的脚本文件在DOMContentLoaded事件触发前执行(即刚刚读取完</html>标签),而且可以保证执行顺序就是他们在页面上出现的顺序
  • 对于内置而不是链接外部脚本的script标签,以及动态生成的script标签,defer属性不起作用

async

  • 解决'阻塞效应'的另一个方法是加入async属性
saync.jpg
  • async属性可以保证脚本下载的同时,浏览器继续渲染,
  • 需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序,哪个脚本先下载结束,就先执行那个.使用async属性的脚本文件中,不应该使用document.write方法

重流和重绘(此部分为转载)

  • 渲染树转换为网页布局,成为'布局流';布局显示到页面的这个过程,称为'绘制' 他们都具有阻塞效应,并且会耗费很多时间和计算资源

  • 页面生成后,脚本操作和样式表操作,都会触发重流和重绘,用户的互动,比如设置了鼠标悬停效果,页面滚动,在输入框中输入文本,改变窗口大小.

  • 重流和重绘并不一定一起发生,重流必然导致重绘,重绘不一定需要重流.比如改变元素的颜色,只会导致重绘,而不会导致重流.改变元素的布局,则会导致重流和重绘.

  • 大多情况下,浏览器会智能判断,将重流和重绘只限制到相关的子树上面,最小化所耗费的代价,而不会全局生成网页

  • 作为开发者,应该尽量设法降低重绘的次数和成本.比如,尽量不要变动高层的DOM元素,而以底层DOM元素的变动代替,再比如,重绘table布局和flex布局,开销都比较大.

    var foo = document.getElementById(‘foobar’);
    
    foo.style.color = ‘blue’;
    foo.style.marginTop = ‘30px’;
    

    上面的代码只会导致一次重绘,因为浏览器会累积DOM 变动,然后一次性执行.
    下面的代码则会导致两次重绘:

    var foo = document.getElementById(‘foobar’);
    
    foo.style.color = ‘blue’;
    var margin = parseInt(foo.style.marginTop);
    foo.style.marginTop = (margin + 10) + ‘px’;
    

    优化技巧:

    • 读取DOM或者写入DOM,尽量写在一起,不要混杂;
    • 缓存DOM信息
    • 不要一项一项的改变样式,而是使用CSS class一次性改变样式
    • 使用document fragment操作DOM
    • 动画时使用absolute定位或fixed定位,这样可以减少对其他元素的影响
    • 只在必要时才显示元素
    • 使用window.requestAnimationFrame(),因为它可以把代码推迟到下一次重流时执行,而不是立即要求页面重流
    • 使用虚拟DOM(virtual DOM)库
    // 重绘代价高
    function doubleHeight(element) {
      var currentHeight = element.clientHeight;
      element.style.height = (currentHeight * 2) + ‘px’;
    }
    
    all_my_elements.forEach(doubleHeight);
    
    // 重绘代价低
    function doubleHeight(element) {
      var currentHeight = element.clientHeight;
    
      window.requestAnimationFrame(function () {
        element.style.height = (currentHeight * 2) + ‘px’;
      });
    }
    
    all_my_elements.forEach(doubleHeight);
    

脚本的动态嵌入(此部分为转载)


['1.js', '2.js'].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  document.head.appendChild(script);
});

这种方法的好处是,动态生成script标签不会阻塞页面渲染,也就不会造成浏览器假死,但是问题在于,这种方法无法保证脚本的执行顺序,哪个脚本文件先下载完成,就先执行哪个,

如果想避免这个问题,可以设置async属性为false

['1.js', '2.js'].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

在这段代码之后加载的脚本,要等待2.js执行完成后再执行

(function() {
  var script,
  scripts = document.getElementsByTagName('script')[0];
  function load(url) {
    script = document.createElement('script');
    script.async = true;
    script.src = url;
    scripts.parentNode.insertBefore(script, scripts);
  }
  load('//apis.google.com/js/plusone.js');
  load('//platform.twitter.com/widgets.js');
  load('//s.thirdpartywidget.com/widget.js');
}());

此外,动态嵌入还有一个地方需要注意,动态嵌入必须等到CSS文件加载完成后,才会去下载外部脚本文件,静态加载就不存在这个问题,script标签指定的外部脚本文件,都是与css文件同时并发下载的.

单线程模型

单线程.jpg
  • js采用的是单线程模型
    • 一次只能运行一个任务,其他任务需要等待前一个任务完成才能工作.
    • 为什么不用多线程呢?
      • 原因是不想浏览器变得复杂,因为多线程需要共享资源,且有可能修改彼此的运行结果
  • h5允许多线程,但是子线程完全受主线程控制,且不得操作DOM,所以这个新标准并没
    • 有改变js单线程的本质.
    • 存在的问题.
      容易造成浏览器假死状态;
单线程问题.jpg
  • js设计者意识到,CPU完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务,等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去,这种机制就是js内部采用的Event Loop

Event Loop

  • Event Loop,指的是一种内部循环,用来排列和处理事件,以及执行函数.是一种程序结果,用于等待和发送信息和事件.
  • 所有任务可以分成两种,一种是同步任务,一种是异步任务,
    • 同步任务,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,

    • 异步任务,不进入主线程,而进入任务队列的任务.只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

  • 下图上面浅蓝色底为同步任务,图下浅红色底为异步任务。

同步异步.jpg
  • 同步模式和异步模式
模式.jpg

异步模式主线程可以运行更多的任务,提高了效率

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,790评论 25 707
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,213评论 11 349
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • Web前端技术由html、css和 javascript 三大部分构成,是一个庞大而复杂的技术体系,其复杂程度不低...
    WEB攻程狮阅读 1,875评论 2 116
  • LZC ,跟你认识了一年半了,我到现在还不知道你真正的名字,你只是在一开始相识的时候告诉过我,你叫LZC! 我记住...
    P尐c阅读 427评论 0 0