這將是一個系列的文章。javascript
正如文章標題所講,從新認識JavaScript,我將把JavaScript從概念到用法總體梳理一遍,中間或許還會夾雜一些我我的的使用經驗以及工具整理。俗話說「溫故而知新」,對基礎知識的紮實掌握必是爲前端大牛不可或缺的條件,共勉之。css
JavaScript(JS)是一種具備函數優先的輕量級,解釋型或即時編譯型的編程語言。最初是做爲開發Web頁面的腳本語言而聞名的,可是如今也被用到了不少非瀏覽器環境中,例如Node.js等。JavaScript是一種基於原型 (注1) 編程、多範式 (注2) 的動態腳本語言,並支持面向對象、命令式和聲明式(如函數式編程)風格。前端
JavaScript的標準是ECMAScript。截止2012年,全部的現代瀏覽器 (注3) 都完整的支持ECMAScript5.1,舊版本的瀏覽器至少支持ECMAScript3標準。2015年6月17日,ECMA國際組織發佈了ECMAScript的第六版,該版本的正式名稱爲ECMAScript2015,但一般被稱爲ECMASctipt6或ES6。自此,ECMAScript每一年發佈一次新標準。java
每一個瀏覽器標籤頁就是其自身用來運行代碼的獨立容器(這些容器用專業術語稱爲「運行環境」)。大多數狀況下,每一個標籤頁中的代碼徹底獨立運行,並且一個標籤頁中的代碼不能影響另外一個標籤頁(或者另外一個網站)中的代碼。這是一個好的安全措施,若是不這樣,黑客就能夠從其餘網站盜取信息。jquery
能夠用安全的方式在不一樣網站/標籤頁中傳送代碼和數據,這些技術後面再講。web
當瀏覽器執行到一段JavaScript代碼時,一般會按照從上往下的順序執行這段代碼。這意味着須要注意代碼的順序。例如:編程
console.log('第一個被執行');
console.log('第二個被執行');
console.log('第三個被執行');
複製代碼
在解釋型語言中,代碼自上而下運行,且實時返回運行結果。在代碼執行前,不轉化爲其餘形式。瀏覽器
編譯型語言在代碼運行前須要先轉化(或編譯)成另一種形式。好比C/C++先被編譯成彙編語言,而後才能由計算機運行。安全
JavaScript是輕量級解釋型語言。服務器
在web開發中,還有服務器端和客戶端代碼這兩個術語。客戶端代碼是在用戶電腦上運行的代碼,在瀏覽一個網頁時,它的客戶端代碼就會被下載,而後由瀏覽器運行並展現。
而服務器端代碼在服務器上運行,瀏覽器將結果下載並展現出來。流行的服務器端web語言包括:PHP、Python、Ruby以及JavaScript。JavaScript也能夠用做服務器端語言,好比如今流行的Node.js環境。
「動態」一詞既能描述客戶端JavaScript,又能描述服務端語言。是指經過按需生成新內容來更新 web頁面/應用,使得不一樣環境下顯示不一樣內容。
沒有動態更新內容的網頁叫作「靜態」頁面,所顯示的內容不會改變。
只須要一個元素<script>
,就能夠將JavaScript添加到HTML頁面中。
</body>
標籤前插入如下代碼:<script> // 在此編寫JavaScript代碼 </script>
複製代碼
<script>
元素替換爲:<script src='script.js'></script>
複製代碼
有時候會在HTML中存在着一絲真實的JavaScript代碼。或許看起來像下面這樣:
<button onclick='sayHello()'>點我</button>
<script> function sayHello () { console.log('Hello'); } </script>
複製代碼
然而請不要這麼作,這將使JavaScript污染到HTML,並且效率低下。對於每一個須要應用JavaScript的按鈕,都得手動添加onclick='sayHello()'
屬性。
能夠用純JavaScript結構來經過一個指令選取全部按鈕。以下:
const buttons = document.querySelectAll('button');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', sayHello);
}
複製代碼
這樣乍看上去比click
屬性要長一些,可是這樣寫會對頁面上全部按鈕生效,不管多少個,添加或刪除,徹底無需修改JavaScript代碼。
要讓腳本調用的時機符合預期,須要解決一系列問題。最多見的問題就是:HTML元素是按其在頁面中出現的次序調用的,若是用JavaScript來管理頁面上的元素,若是JavaScript加載於欲操做的HTML元素以前,則代碼將出錯。
解決此問題的舊方法:把腳本元素放在文檔體的底部(標籤以前,與之相鄰),這樣腳本就能夠在HTML解析完畢後加載了。此方案的問題是:只有全部的HTML元素加載完成後纔開始腳本的加載/解析過程。對於有大量JavaScript代碼的大型網站,可能會帶來顯著的性能損耗。
上述的腳本阻塞問題實際有兩種解決方案:async
和defer
。
先來看async
和defer
的定義。async
:規定異步執行腳本(只限外部腳本);defer
:規定是否對腳本進行延遲,知道頁面加載爲止。
瀏覽器遇到async
腳本時不會阻塞頁面渲染,而是直接下載後運行。這樣腳本的運行次序就沒法控制,只是腳本不會阻止剩餘頁面的顯示。當頁面中腳本之間彼此獨立,且不依賴於本頁面的其餘任何腳本時,async
是最理想的選擇。
好比頁面要加載如下三個腳本:
<script src='js/verdor/jquery.js' async></script>
<script src='js/script1.js' async></script>
<script src='js/script2.js' async></script>
複製代碼
三者調用順序是不肯定的,jquery可能在script1和script2以前或者以後嗲用,若是這樣,這兩個腳本中依賴jauery的函數將產生錯誤。
解決這一問題可用defer屬性,腳本將按照頁面中出現的順序加載和運行:
<script src='js/verdor/jquery.js' defer></script>
<script src='js/script1.js' defer></script>
<script src='js/script2.js' defer></script>
複製代碼
腳本調用策略小結:
async
。defer
,將關聯的腳本按所需順序置於HTML中。像其餘語言同樣,JavaScript也能夠添加註釋。註釋只爲本身或同事提供代碼如何工做的指引。註釋很是有用,並且應該常用,尤爲是在大型項目中。
註釋分兩類:
// 這是一條註釋
複製代碼
/*
和*/
之間添加多行註釋/* 我也是 一條註釋 */
複製代碼
這一篇文章從理論開始,介紹了爲何要使用JavaScript,以及它能作什麼事情。下一篇將按部就班,繼續深刻JavaScript。
當開發一個大型項目時,會常常遇到在外部腳本中按需加載外部腳本的狀況。若是每次加載時,都寫一遍類似的加載腳本的代碼,會增長很多開發與維護的成本。當有這樣的需求時,若是直接將這一類功能相似的代碼封裝成一個腳本,將會成倍的提升開發效率。
像下面這樣:
/* * @Author: 伊麗莎不白 * @Date: 2019-07-23 14:46:44 * @Last Modified by: 伊麗莎不白 * @Last Modified time: 2019-07-23 15:12:24 */
class ResLoader {
constructor () {
this.loaded = false;
}
_createElement (type, url) {
let element = document.createElement(type);
switch (type) {
case 'link':
element.setAttribute('type', 'text/css');
// 指明被連接文檔對於當前文檔的關係
element.setAttribute('rel', 'stylesheet');
element.setAttribute('href', url);
break;
case 'script':
// 定義script元素包含或src引用的腳本語言
element.setAttribute('type', 'text/javascript');
element.setAttribute('src', url);
break;
}
return element;
}
_addCallback (target, callback) {
let thisScope = this;
target.onload = target.onreadystatechange = function () {
// readyState屬性返回當前文檔的狀態
if (!thisScope.loaded && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) {
thisScope.loaded = true;
// 防止內存泄漏
this.onload = this.onreadystatechange = null;
if (callback && typeof callback === 'function') {
callback();
}
}
};
}
load (url, type, callback) {
if (type === 'link' || type === 'script') {
let head = document.getElementsByTagName('head')[0] || document.documentElement;
let res = this._createElement(type, url);
if (res) {
this._addCallback(res, callback);
head.appendChild(res);
this.loaded = true;
return res;
}
}
}
}
複製代碼
注1:基於原型:基於原型的語言具備所謂原型對象的概念。原型對象能夠做爲一個模版,新對象能夠從中得到原始的屬性。任何對象均可以制定其自身的屬性,既能夠是建立時也能夠是運行時建立。並且,任何對象均可以做爲另外一個對象的原型,從而容許後者共享前者的屬性。
注2:範式:編程範式是一類典型的編程風格,例如函數式編程、面向對象編程等爲不一樣的編程範式。在面向對象編程中,認爲程序是一系列互相做用的對象,而在函數式編程中一個程序會被看做是一個無狀態的函數計算的序列。
注3:現代瀏覽器:現代瀏覽器是指該瀏覽器可以理解和支持HTML和XHTML,CSS,ECMAScript及W3C DOM標準。