-
JavaScript的发展历史
JavaScript因为互联网而生,紧随着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。
1990年底——万维网问世
欧洲核能研究组织(CERN)科学家Tim Berners-Lee,发明了万维网(World Wide Web)。
1992年底——第一个浏览器
美国国家超级电脑应用中心(NCSA)开始开发世界第一个独立的浏览器,叫做Mosaic。
1994年底——Navigator1.0版浏览器发布
1994年10月NCSA的一个主要程序员Marc Andreessen联合风险投资家Jim Clark,成立了Mosaic通信公司(Mosaic Communications),不久后改名为Netscape。这家公司的方向,就是在Mosaic的基础上,开发面向普通用户的新一代的浏览器Netscape Navigator。
1994年12月,Navigator发布了1.0版,市场份额一举超过90%。
1995年12月4日——Netscape公司与Sun公司联合发布了JavaScript语言
1995年,Netscape公司雇佣了程序员Brendan Eich开发这种网页脚本语言。Brendan Eich有很强的函数式编程背景,希望以Scheme语言(函数式语言鼻祖LISP语言的一种方言)为蓝本,实现这种新语言。
1995年5月,Brendan Eich只用了10天,就设计完成了这种语言的第一版。
Netscape公司的这种浏览器脚本语言,最初名字叫做Mocha,1995年9月改为LiveScript。12月,Netscape公司与Sun公司(Java语言的发明者和所有者)达成协议,后者允许将这种语言叫做JavaScript。这样一来,Netscape公司可以借助Java语言的声势,而Sun公司则将自己的影响力扩展到了浏览器。
1995年12月4日,Netscape公司与Sun公司联合发布了JavaScript语言。
1996年3月——Navigator 2.0浏览器正式内置了JavaScript脚本语言
1996年8月——Netscape公司主导权危机
1996年8月,微软模仿JavaScript开发了一种相近的语言,取名为JScript(JavaScript是Netscape的注册商标,微软不能用),首先内置于IE 3.0。Netscape公司面临丧失浏览器脚本语言的主导权的局面。
1997年7月——ECMAScript 1.0发布
1996年11月,Netscape公司决定将JavaScript提交给国际标准化组织ECMA(European Computer Manufacturers Association),制定浏览器脚本语言的标准。
1998年6月——ECMAScript 2.0版发布
1999年12月——ECMAScript 3.0版发布
2007年10月——ECMAScript 4.0版草案发布(产生分歧)
2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级,预计次年8月发布正式版本。草案发布后,由于4.0版的目标过于激进,各方对于是否通过这个标准,发生了严重分歧。以Yahoo、Microsoft、Google为首的大公司,反对JavaScript的大幅升级,主张小幅改动;以JavaScript创造者Brendan Eich为首的Mozilla公司,则坚持当前的草案。
2008年7月——废除4.0版本(解决矛盾)
2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA开会决定,中止ECMAScript 4.0的开发(即废除了这个版本),将其中涉及现有功能改善的一小部分,发布为ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为Harmony(和谐)。会后不久,ECMAScript 3.1就改名为ECMAScript 5。
2009年12月——ECMAScript 5.0版正式发布(规划未来)
2009年12月,ECMAScript 5.0版正式发布。Harmony项目则一分为二,一些较为可行的设想定名为JavaScript.next继续开发,后来演变成ECMAScript 6;一些不是很成熟的设想,则被视为JavaScript.next.next,在更远的将来再考虑推出。TC39的总体考虑是,ECMAScript 5与ECMAScript 3基本保持兼容,较大的语法修正和新功能加入,将由JavaScript.next完成。当时,JavaScript.next指的是ECMAScript 6。第六版发布以后,将指ECMAScript 7。TC39预计,ECMAScript 5会在2013年的年中成为JavaScript开发的主流标准,并在此后五年中一直保持这个位置。
2011年6月——ECMAScript 5.1版发布
2011年6月,ECMAscript 5.1版发布,并且成为ISO国际标准(ISO/IEC 16262:2011)。到了2012年底,所有主要浏览器都支持ECMAScript 5.1版的全部功能。
2013年12月——ECMAScript 6草案发布
2013年3月,ECMAScript 6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。
2013年12月,ECMAScript 6草案发布。然后是12个月的讨论期,听取各方反馈。
2015年6月——ECMAScript 2015(ECMAScript 6)正式发布
2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”。这是因为TC39委员会计划,以后每年发布一个ECMAScirpt的版本,下一个版本在2016年发布,称为“ECMAScript 2016”。
2016年6月——ECMAScript2016正式发布
2016年6月,《ECMAScript 2016 标准》发布。与前一年发布的版本相比,它只增加了两个较小的特性。
-
浏览器的渲染机制
1.解析HTML标签,构建DOM树。
2.解析CSS标签,构建CSSOM树。
3.把DOM和CSSOM组合成渲染树(render tree)。
4.在渲染树的基础上进行布局,计算每个节点的几何结构。
5.把每个节点绘制到屏幕上(painting)。
-
CSS、JS 在 HTML 中如何放置
1.CSS:link标签引入放置head标签中
通常,将CSS写成一个单独的CSS文件,然后通过link引入进来,放在head标签中。
2.javascript:放置body标签内的最后
一般放在body标签内的最后,通常会优先选择显示内容,因为javascript是脚本语言,在遇到script标签的时候会解析脚本,由此带来会延迟DOM树的构建以及后面的渲染,所以屏幕可能会呈现短时间的白屏,影响用户体验,所以通常把script标签放在最后。
-
白屏与无样式闪烁(FOUC)
1.白屏:
(1)如果JS放置head中,在加载js文件的过程中,会导致一些时间的白屏,因为会先停止DOM树和CSSOM的构建,会导致所有浏览器白屏。
(2)对于页面会等到html和css加载完成后再进行渲染的浏览器(如IE),等待CSS加载会造成白屏。具体情况如CSS放置在body标签底部;@import引用的CSS会等到页面全部被下载完再被加载。
2.FOUC(Flash of Unstyled Content):
(1)对于渲染不会等待 CSS 完全加载后才开始的浏览器(如Firefox),如果 CSS 文件没有放在页面开始,就会出现先加载了无样式的页面,在 CSS 出现后又刷新为有样式的页面,因此而出现的就是FOUC 现象。
(2)此外,如果把样式放在底部,对于IE等浏览器,在某些场景下(点击链接,输入URL,使用标签进入等),会出现FOUC现象(逐步加载无样式内容,等CSS加载后页面突然展现样式),对于FireFox等会一直表现出FOUC。
-
repaint和 reflow
1.什么是 repaint和 reflow
对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint。
2.引起Repain和Reflow的一些操作
Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么,但是如果 reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。
所以,下面这些动作有很大可能会是成本比较高的。
- 当你增加、删除、修改 DOM 结点时,会导致 Reflow 或 Repaint。
- 当你移动 DOM 的位置,或是搞个动画的时候。
- 当你修改 CSS 样式的时候。
- 当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。
- 当你修改网页的默认字体时。
注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发现位置变化。
所以reflow必然伴随着repaint,repaint不一定有reflow!
3.如何优化
Reflow是不可避免的,只能将Reflow对性能的影响减到最小。
(1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className:
//不好的写法 var div1=document.getElementById('div1'); div1.style.color='red'; div1.style.top=50+'px'; div1.style.fontSize=5+'em'; /*推荐写法 在CSS文件里创建一个类样式备用,如: .theclassname { color: red; top: 50px; font-size: 5em; }*/ var div1=document.getElementById('div1'); div1.className+=' theclassname';
(2)把 DOM 离线后修改。如:
a> 使用 documentFragment 对象在内存里操作 DOM。
b> 先把 DOM 给 display:none (有一次 repaint),然后你想怎么改就怎么改。
比如修改 100 次,然后再把他显示出来。
c> clone 一个 DOM 节点到内存里,然后想怎么改就怎么改,改完和在线的那个的交换一下。
(3)不要把 DOM 节点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写
这个结点的属性。
(4)尽可能的修改层级比较低的 DOM节点。当然,改变层级比较底的 DOM节点有可能会造成大
面积的 reflow,但是也可能影响范围很小。
(5)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是会
大大减小 reflow 。
(6)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的
重新布局。
-
CSS/JS对DOM渲染的影响
- CSS(外链或内联)会阻塞整个DOM的渲染(Rendering),然而DOM解析(Parsing)会正常进行。
- 很多浏览器中,CSS会延迟脚本执行和DOMContentLoaded事件。
- JS(外链或内联)会阻塞后续DOM的解析(Parsing),后续DOM的渲染(Rendering)也将被阻塞。
- JS前的DOM可以正常解析(Parsing)和渲染(Rendering)。
-
实现JS脚本异步加载(相对于DOM解析)
- 蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。
这里实现JS脚本异步加载是相对于DOM解析来说的,在上图第一行中,普通的<script>……</script>
标签里的内容加载和执行时都会阻塞DOM解析。
那么如何实现JS加载时不影响DOM解析,也就是如何实现JS加载异步?
(1)有 async,加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步)。
<script async src="script.js"></script>
(2)有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。
<script defer src="myscript.js"></script>
defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的
-
JS脚本同步执行与异步执行
- Javascript语言的执行环境是"单线程",所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
- 这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
- 为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
1.同步执行
默认的js是同步加载的,这里的“加载”可以理解成是解析、执行,而不是“下载”,在最新版本的浏览器中,浏览器对于代码请求的资源都是瀑布式的加载,而不是阻塞式的,但是js的执行总是阻塞的。"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
2.异步执行
"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
3.实现异步执行
实现异步执行的方法有很多,这里暂时讲以下几种:
……(作者累了,休息一下)
网友评论