JavaScript的發(fā)展歷史
JavaScript因瀏覽器而生冕末,回顧它的歷史要從瀏覽器的歷史講起姆另。
- 1990年底喇肋,歐洲核能研究組織(CERN)科學家Tim Berners-Lee,在互聯(lián)網的基礎上發(fā)明了萬維網(World Wide Web)迹辐。但是只能使用命令行操作蝶防,非常不方便。
- 1992年底明吩,美國國家超級電腦應用中心(NCSA)開發(fā)了一個叫做Mosaic的獨立瀏覽器间学。這是人類歷史上第一個瀏覽器,從此網頁可以在圖形界面窗口瀏覽印荔。
- 1994年10月低葫,NCSA程序員Marc Andreessen聯(lián)合風險投資家Jim Clark成立了Mosaic通信公司,而后改名為Netscape躏鱼。在Mosaic瀏覽器的基礎上開發(fā)新一代瀏覽器Netscape Navigator氮采。
- 1994年12月,Navigator發(fā)布1.0版染苛,很快Netscape公司認為Navigator瀏覽器需要一種能嵌入網頁腳本的語言來控制瀏覽器行為鹊漠。當時網速很慢且價格昂貴,有些操作不宜在服務器端完成茶行,如用戶名忘記填寫“用戶名”就點擊了發(fā)送躯概,到服務器再發(fā)現(xiàn)就太晚了。所以需要在網頁中嵌入小程序讓瀏覽器檢查每一欄是否都已填寫畔师。
管理層希望這種瀏覽器腳本語言:功能不需要太強娶靡,語法簡單,容易學習和部署看锉。那一年正逢Sun公司的Java語言問世姿锭,Netscape與Sun公司合作塔鳍,決定腳本語言語法接近Java,并支持Java程序呻此。 - 1995年5月轮纫,程序員Brendan Eich以Scheme語言為藍本設計完成了這種語言的第一版。
- 1995年9月焚鲜,這種瀏覽器腳本語言由最初的Mocha更名為LiveScript.
- 1995年12月掌唾,這種瀏覽器腳本語言的命名由最初的Mocha到LiveScript,最終改名為JavaScript忿磅。
- 1995年12月4日糯彬,Netscape與Sun公司聯(lián)合發(fā)布了JavaScript語言。
- 1996年3月葱她,Navigator 2.0瀏覽器正式內置了JavaScript腳本語言撩扒。
- 1996年8月,微軟模仿JavaScript開發(fā)了一種相近的語言取名為JScript览效,內置IE 3.0却舀。
- 1996年11月虫几,Netscape決定將JavaScript提交給國際標準化組織 ECMA锤灿,希望JavaScript成為國際標準,以此抵抗微軟辆脸。
- 1997年7月但校,ECMA發(fā)布(ECMA-262)的第一版,規(guī)定了瀏覽器腳本語言的標準啡氢,并將此語言命名為ECMAScript状囱。也就是ECMAScript 1.0版。
(ECMAScript和JavaScript的關系是倘是,前者是后者的規(guī)格亭枷,后者是前者的一種實現(xiàn)。) - 1998年6月搀崭,ECMAScript 2.0版發(fā)布叨粘。
- 1999年12月,ECMAScript 3.0版發(fā)布瘤睹,成為JavaScript的通行標準。
- 2007年10月轰传,ECMAScript 4.0版草案發(fā)布驴党,對3.0版做了大幅升級获茬,預計次年8月發(fā)布正式版本倔既。由于以Yahoo鹏氧,Microsoft,Google為首的大公司反對大幅升級主張小幅改動度帮,4.0版未能正式發(fā)布。
- 2008年7月笨篷,由于各方分歧太大,ECMA決定終止ECMAScript 4.0的開發(fā)率翅,改善其一小部分练俐,發(fā)布為ECMAScript3.1腺晾。
- 2009年12月,ECMAScript5.0正式發(fā)布辜贵。
- 2011年6月悯蝉,ECMAscript 5.1版發(fā)布,并且成為ISO國際標準(ISO/IEC 16262:2011)托慨。到了2012年底鼻由,所有主要瀏覽器都支持ECMAScript 5.1版的全部功能。
- 2013年3月厚棵,ECMAScript 6草案凍結蕉世,不再添加新功能。
- 2013年12月婆硬,ECMAScript 6草案發(fā)布狠轻。然后是12個月的討論期,聽取各方反饋彬犯。
- 2015年6月向楼,ECMAScript 6正式發(fā)布,并且更名為“ECMAScript 2015”躏嚎。
- 2016年6月蜜自,《ECMAScript 2016標準》發(fā)布。
瀏覽器的渲染機制
- 解析 HTML 標簽, 構建 DOM 樹:
渲染引擎開始解析HTML文檔卢佣,轉換樹中的html標簽或JS生成的標簽到DOM節(jié)點重荠,它被稱為--內容樹 - 解析 CSS 標簽, 構建 CSSOM 樹
解析CSS(包括外部CS文件和樣式元素以及JS生成的樣式) - 把 DOM 和 CSSOM 組合成 渲染樹 (render tree)
根據(jù)CSS選擇器計算出節(jié)點的樣式,創(chuàng)建另一個樹--節(jié)點樹 - 布局渲染樹
從根節(jié)點開始計算每個元素的大小虚茶,位置等戈鲁,給每個節(jié)點所應該出現(xiàn)在屏幕上的精確坐標仇参。 - 繪制渲染樹
遍歷渲染樹,把每個節(jié)點繪制到屏幕上
白屏&FOUC (Flash of Unstyled Content) 無樣式內容閃爍
不同的瀏覽器資源加載方式不同婆殿,或者有不同的渲染機制诈乒,所以會出現(xiàn)白屏或者FOUC。
- 白屏:對于Chrome瀏覽器婆芦,它的處理機制是白屏怕磨。Chrome瀏覽器會等所有CSS加載并解析完成,CSSOM計算完成后才會把全部的頁面展示出來消约,所以在這個解析時間中頁面會出現(xiàn)白屏肠鲫。
- FOUC:對于Firefox瀏覽器,它的處理機制是FOUC或粮。Firefox瀏覽器在解析html時不會等待CSS的加載导饲,所以頁面會先將解析的html展現(xiàn)在頁面上。當CSS加載完成后氯材,頁面的內容樣式會根據(jù)你所設定的樣式發(fā)生改變渣锦,也就是無樣式內容閃爍。
樣式氢哮、JS 在 HTML 中如何放置?
- 樣式可以直接寫在<head>的<style>標簽中命浴,或者在<head>中使用<link>標簽引入外部樣式文件贱除,需要優(yōu)先加載。
-
JS放在</body>標簽之前碍讯,通常有兩種引入方式扯躺,直接放在<script>標簽中录语,或通過<script src=" ">引入外部JS文件。另虽缕,也可以放入<head>中,同時使用defer或async來延遲或異步加載JS氮趋。
推薦引入方式
將JS放在底部原因:
腳本加載后會立刻執(zhí)行,JS的加載會影響頁面內容的渲染 - 腳本會阻塞后面內容的呈現(xiàn)
- 腳本會阻塞其后組件的下載
對于圖片和CSS, 在加載時會并發(fā)加載(如一個域名下同時加載兩個文件). 但在加載 JavaScript 時,會禁用并發(fā),并且阻止其他內容的下載. 所以把 JavaScript 放入頁面頂部也會導致 白屏 現(xiàn)象.
Repaint 和 Reflow
頁面設計中诉植,不可避免的需要進行repaint和reflow昵观。
- Repaint:重繪
當渲染樹中的一些元素需要更新屬性啊犬,而 這些屬性只是影響元素的外觀,風格缤至,而不會影響布局的康谆,比如background-color沃暗。就稱為重繪。 - Reflow:回流
當渲染樹中的一部分(或全部)因為元素的規(guī)模尺寸嚼黔,布局惜辑,隱藏等改變而需要重新構建,這就稱為回流碎节。每個頁面至少需要一次回流抵卫,就是在頁面第一次加載的時候。
回流必引起重繪殖氏,而重繪不一定引起回流雅采。
在性能優(yōu)先的前提下,性能消耗reflow大于repaint纲堵,下文將重點總結關于reflow的觸發(fā)場景及優(yōu)化闰渔。
- 觸發(fā)reflow的場景:
- 調整窗口大小(Resizing the window)
- 改變字體(Changing the font)
- 增加或者移除樣式表(Adding or removing a stylesheet)
- 內容變化茂附,比如用戶在input框輸入文字(Content changes, such as a user typing text in)
- 激活CSS偽類营曼,比如
:hover
(Activation of CSS pseudo classes such as:hover
) - 操作class屬性(Manipulating the class attribute)
- 腳本操作DOM(A script manipulating the DOM)蒂阱,如appendChild
- 設置style屬性的值(Setting a property of the style attribute)
repaint
- 優(yōu)化
對于性能狂塘,Opera列出 reflow 和 repaint 是減緩JavaScript的三大主要原因之一,所以如何避免 reflow 或將它們對性能的影響降到最低妈踊?- 如果想設定元素的樣式廊营,通過改變元素的
class
名(盡可能在DOM樹的末端)(Change classes on the element you wish to style (as low in the dom tree as possible ))
不要一條一條的修改DOM的樣式萝勤。最好預先定義好CSS的class,然后修改DOM的className. - 避免設置多項內聯(lián)樣式(Avoid setting multiple inline styles)
因為每個都會造成回流邀窃,樣式應該合并在一個外部類,這樣當該元素的class屬性可被操控時僅會產生一個reflow鞍历。 - 應用元素的動畫,使用
position
屬性的fixed
值或absolute
值(Apply animations to elements that are position fixed or absolute)
動畫效果應用到position屬性為absolute或fixed的元素上惧蛹,它們不影響其他元素的布局香嗓,這時修改它們的CSS會大大的減少reflow - 權衡平滑和速度(Trade smoothness for speed)
Opera還建議我們犧牲平滑度換取速度,其意思是指您可能想每次1像素移動一個動畫沧烈,但是如果此動畫及隨后的回流使用了100%的CPU像云,動畫就會看上去是跳動的迅诬,因為瀏覽器正在與更新回流做斗爭。動畫元素每次移動3像素可能在非吵颓福快的機器上看起來平滑度低了俏蛮,但它不會導致CPU在較慢的機器和移動設備中抖動。 - 避免使用table布局(Avoid tables for layout)
因為即使一些小的變化將導致表格(table)中的所有其他節(jié)點回流锨并。 - 把 DOM 離線后修改第煮。如:
a> 使用 documentFragment 對象在內存里操作 DOM抑党。
b> 先把 DOM 給 display:none (有一次 repaint),然后你想怎么改就怎么改害晦。比如修改 100 次壹瘟,然后再把他顯示出來鳄逾。
c> clone 一個 DOM 節(jié)點到內存里,然后想怎么改就怎么改殴俱,改完后,和在線的那個的交換一下线欲。 - 不要把 DOM 節(jié)點的屬性值放在一個循環(huán)里當成循環(huán)里的變量李丰。不然這會導致大量地讀寫這個結點的屬性。
會及時更新好的優(yōu)化方法...
- 如果想設定元素的樣式廊营,通過改變元素的
如何異步加載腳本
<script>的六個屬性
-
async
:異步腳本可選逆屡,表示立即下載腳本踱讨,但不妨礙頁面中其他操作痹筛,只對外部腳本有效。 -
defer
:延遲腳本可選谣旁,表示腳本可以延遲到文檔完全被解析和顯示之后在執(zhí)行滋早,只對外部腳本有效杆麸。 -
charset
:可選,表示通過src
屬性指定的代碼的字符集饼问,由于大多數(shù)瀏覽器會忽略這個值揭斧,因此屬性很少用。 -
src
:可選盅视,表示包含要執(zhí)行的外部文件萧吠。 -
language
:已廢棄。 -
type
:可選,可以看成是language
的替代屬性狰腌。
沒有defer
或async
,瀏覽器會立即加載并執(zhí)行指定腳本瑰枫,“立即”指的是在渲染該script
標簽之下的文檔元素之前光坝,也就是說不等待后續(xù)載入的文檔元素甥材,讀到就加載并執(zhí)行。
<script async src="script.js"></script>
鸳惯,加載和渲染后續(xù)文檔元素的過程將和script.js
的加載執(zhí)行并行進行(異步)芝发。
異步腳本一定會在頁面的load
事件前執(zhí)行
<script defer src="script.js"></script>
苛谷,加載后續(xù)文檔元素的過程和script.js的加載并行進行(異步),但script.js
的執(zhí)行要在所有元素解析完成后独悴,DOMContentLoaded事件觸發(fā)之前完成绵患。
-
defer
:腳本延遲到文檔解析和顯示后執(zhí)行悟耘,有順序 -
async
:不保證順序(用于廣告侥锦,統(tǒng)計之類,不會影響到頁面的元素)