使用 JavaScript 作為編程語言
前世今生
????JavaScript 在 1995年 由Netscape公司的 Brendan Eich,在網(wǎng)景導(dǎo)航者瀏覽器上首次設(shè)計(jì)實(shí)現(xiàn)而成。1996 年 ?11 月,網(wǎng)景公司將 JavaScript 提交給 Ecma 國際進(jìn)行標(biāo)準(zhǔn)化。????????????JavaScript的標(biāo)準(zhǔn)是 ECMAScript(European ?Computer Manufacturers Association ?歐洲計(jì)算機(jī)制造協(xié)會(huì))。截至2012年,所有瀏覽器都完整的支持 ECMAScript 5.1,舊版本的瀏覽器至少支持 ECMAScript ?3 標(biāo)準(zhǔn)。2015年6月17日,ECMA國際組織發(fā)布了ECMAScript的第六版,該版本正式名稱為ECMAScript ?2015,但通常被稱為ECMAScript 6或者ES2015。
????JavaScript ?是一門動(dòng)態(tài)語言,動(dòng)態(tài)語言是在 運(yùn)行時(shí) 確定數(shù)據(jù)類型的語言。變量使用之前不需要類型聲明,通常變量的類型是被賦值的那個(gè)值的類型,即變量的類型是由其應(yīng)用上下文確定的。 ?對(duì)應(yīng)的靜態(tài)語言是在編譯時(shí)變量的數(shù)據(jù)類型即可確定的語言,多數(shù)靜態(tài)類型語言要求在使用變量之前必須聲明數(shù)據(jù)類型。
????我們知道程序運(yùn)行的本質(zhì),就是CPU不斷識(shí)別并計(jì)算出一組指令集(機(jī)器碼)結(jié)果。使用編譯器(如Java),可以事前將特定編程語言編碼成中間代碼,在編碼為機(jī)器碼,再計(jì)算結(jié)果。使用解釋器(如 ?JavaScript),則是在程序運(yùn)行時(shí)逐段讀取代碼,并直接生成機(jī)器碼計(jì)算結(jié)果。常見的 JavaScript 解釋器:
由 Google 開發(fā)的開源 JavaScript V8 引擎 on Google Chrome;
由 Mozilla 開發(fā)的開源 JavaScript Spider Monkey 引擎 on 在 Mozilla Firefox;
由 Apple 開發(fā)的 JavaScriptCore 引擎 on Safari;
????2012年 10月微軟開發(fā)的自由和開源的編程語言 TypeScript 宣發(fā)。TypeScript 設(shè)計(jì)目標(biāo)是開發(fā)大型應(yīng)用,它可以編譯成純 JavaScript,編譯出來的 JavaScript 可以運(yùn)行在任何瀏覽器上。一般編譯器的工作步驟:
1. 詞法分析:將文本字符流組織成有意義的詞素序列,并轉(zhuǎn)化為詞法單元(key,value)序列;并記錄到符號(hào)表中(符號(hào)表記錄了每一個(gè)變量的名字,及其值、類型、作用域等)
2. 語法分析:使用一棵??語法樹來重新組織詞法單元序列,樹能夠很好地表示這個(gè)序列的執(zhí)行優(yōu)先級(jí);
3. 語義分析:重新檢查符號(hào)表和??語法樹是否和約定的編程語言語法一致(比如數(shù)組的下標(biāo)一定要是整數(shù)),它同時(shí)也收集類型信息存放到符號(hào)表中;
4. 中間代碼生成:完成語義分析后,應(yīng)當(dāng)能輕易生成一種類機(jī)器的中間代碼,并且易于翻譯成目標(biāo)代碼(機(jī)器碼);
5. 代碼優(yōu)化:編譯器將會(huì)改進(jìn)中間代碼
6. 目標(biāo)代碼生成;

聲明變量,并使用字面量進(jìn)行賦值
??使用 let、const、var 聲明變量
通過?? let 和 const 聲明的變量和常量具有塊作用域。這意味著它們只在 let 和 const 語句所在的代碼塊中有定義(如函數(shù)、if/while/for 語句等);
并且在聲明前使用 let const 的變量將會(huì)報(bào)錯(cuò),原因是此時(shí)的變量正在暫存性死區(qū),它們并不能像 var 變量一樣優(yōu)先在頂部進(jìn)行聲明,變量提升;
使用?? var ?聲明的變量僅僅在函數(shù)中具有作用域,如果在函數(shù)體外部使用 var ,則會(huì)聲明全局變量。
??可以賦值的七種字面量類型
(六種)基礎(chǔ)類型:Number、String、Boolean、Symbol、undefined、null;
(一種)復(fù)雜類型:Object;
Js中的數(shù)字采用64位來存儲(chǔ)(1bit ?符號(hào)位、11bit 指數(shù)位、52bit 有效數(shù)位)即整數(shù)最值范圍是 [-2~53 - 1,2~53 - 1];IEEE ?754規(guī)定,有效數(shù)字第一位默認(rèn)總是1,不保存在64位浮點(diǎn)數(shù)之中;由于小數(shù)在轉(zhuǎn)換成二進(jìn)制時(shí)在數(shù)學(xué)上是無限位的,實(shí)際中會(huì)被截?cái)鄰亩鴮?dǎo)致了小數(shù)精度丟失;
??轉(zhuǎn)換字面量的類型
可以使用 Number、String、Boolean 轉(zhuǎn)換函數(shù)將某個(gè)字面量顯式轉(zhuǎn)化為對(duì)應(yīng)類型;
也可以使用 +、-、!這些運(yùn)算符進(jìn)行隱式轉(zhuǎn)換;
將對(duì)象轉(zhuǎn)換成??基礎(chǔ)類型:使用 valueOf 函數(shù)看看能否返回基礎(chǔ)類型,如果還是得到對(duì)象(一般是它本身),將會(huì)再使用 toString 函數(shù);
轉(zhuǎn)換成數(shù)字:對(duì)于空字符串、boolean false、null 都將會(huì)轉(zhuǎn)換成 number 0,而非數(shù)字的字符串、undefined 都將會(huì)轉(zhuǎn)換成 number NaN;
轉(zhuǎn)換成字符串:都會(huì)轉(zhuǎn)變?yōu)閷?duì)應(yīng)字面量的字符串,如 '123'、'false' 等;
轉(zhuǎn)換成布爾:對(duì)于空字符串、Number 0 NaN、undefined、null 都將會(huì)轉(zhuǎn)換成 false;
??對(duì)字面量進(jìn)行運(yùn)算
將運(yùn)算內(nèi)容均轉(zhuǎn)換為基礎(chǔ)數(shù)字類型(字符串將會(huì)轉(zhuǎn)換成字符串、NaN運(yùn)算結(jié)果始終為NaN)進(jìn)行算數(shù),并返回結(jié)果;

聲明一個(gè)對(duì)象
????對(duì)象是一組屬性的無序集合;鍵只能是 String 簡單數(shù)據(jù)類型;可以使用字面量 {}、Object.create 方法、new 關(guān)鍵字 + 函數(shù)對(duì)象 來創(chuàng)建對(duì)象;
??使用原型對(duì)象來實(shí)現(xiàn)繼承
任意對(duì)象實(shí)例上都存在 __proto__ 其構(gòu)造函數(shù)的唯一原型對(duì)象 { constructor: 此對(duì)象 }
任意函數(shù)對(duì)象上都存在 prototype 變量,指向其唯一原型對(duì)象
構(gòu)造函數(shù),其實(shí)例,其唯一原型對(duì)象,通過 proto 構(gòu)成了三角關(guān)系;
原型對(duì)象和普通對(duì)象一樣,都是 Object 的實(shí)例,它們都指向 Object 的原型對(duì)象,并且這個(gè)原型對(duì)象的 __proto__ 指向 null;這種原型對(duì)象形成的鏈表,就是所謂的原型鏈;我們可以修改任意構(gòu)造函數(shù)原型對(duì)象的指向,來實(shí)現(xiàn)繼承;
任意構(gòu)造函數(shù)也是一種特殊的對(duì)象「arguments, length, name」,包括 Object 都是 Function 的實(shí)例。它們都指向 Function 的原型對(duì)象。Function 函數(shù)本身作為一個(gè)實(shí)例,它的 proto 還是它自己的原型對(duì)象;
??關(guān)鍵字 new 的工作過程
??ES6 中的 Map 對(duì)象
鍵可以是其他數(shù)據(jù)類型,
可以進(jìn)行迭代、獲取長度、展開等,
值鍵對(duì)具有順序性
??ES6 中的 WeakMap 對(duì)象
鍵只能是對(duì)象的引用
,當(dāng)其他地方都不再使用這些引用后會(huì)釋放內(nèi)存,特別適合臨時(shí)計(jì)算
??ES6 中的迭代對(duì)象
需要實(shí)現(xiàn) Symbol.Iterable 方法(接口隔離原則)
??ES6 中的 Proxy 對(duì)象
const proxy = new Proxy(target, handlers: { ...get/set })
1. 我們執(zhí)行代理對(duì)象的方法達(dá)到操縱對(duì)象,而不是直接調(diào)用原對(duì)象的方法;
2. 代理將會(huì)監(jiān)聽整個(gè)對(duì)象,而不像 Object.defineProperty 需要循環(huán)對(duì)象屬性;
??閉包
我們討論一個(gè)函數(shù)對(duì)象,我們就是在討論它的??函數(shù)代碼,以及圍繞函數(shù)上下文的??所有其他引用;這種函數(shù)對(duì)象與作用域(即一組變量綁定)組合起來解析函數(shù)變量的機(jī)制,在計(jì)算機(jī)科學(xué)文獻(xiàn)中被稱作閉包;
??通過組合而不是繼承來設(shè)計(jì)對(duì)象
我們可以使用 Function.apply(target, array) 來使得 Function() 變?yōu)?target.Function(),其中的 this 指向 target;通過 apply 我們可以組合任意個(gè)工具類對(duì)外暴露成一個(gè)通用工具類
??箭頭函數(shù)
沒有 this argument super 關(guān)鍵詞,不能使用 new 調(diào)用、沒有原型;
??函數(shù)中的 this
指向 global;但是當(dāng)使用 . 符號(hào)運(yùn)行對(duì)象中的函數(shù)時(shí),則是指向這個(gè)對(duì)象;

模塊化方案
??在 html 中劃分 js 文件
在 html 中使用 <script /> 作為模塊劃分基礎(chǔ);這樣做會(huì)導(dǎo)致文件中的聲明將對(duì)同一 html 中的所有腳本可見;
也可以在 html 中使用 <script type='module'/> 作為模塊劃分基礎(chǔ);這樣做會(huì)導(dǎo)致文件中的聲明變?yōu)樗接行再|(zhì);
??在 js 中 劃分其他 js 文件
可以在瀏覽器中采用
Async Module Definition
方案;
1. 第三方庫 RequireJs 中的 define/require 配合回調(diào)函掉函數(shù)異步加載Js代碼;
在 Node 環(huán)境中采用 CommonJs 2009方案;即在運(yùn)行時(shí)
動(dòng)態(tài)同步加載模塊內(nèi)容;
1. 每一個(gè)文件就是一個(gè)模塊,擁有自己獨(dú)立的作用域,變量,以及方法等,對(duì)其他的模塊都不可見;
2. 每個(gè)文件中使用 module 對(duì)象變量代表當(dāng)前模塊,它的 exports 屬性是對(duì)外的接口;暴露了內(nèi)容的淺拷貝;
以ES6 import、export 關(guān)鍵字劃分模塊,在編譯時(shí)加載模塊內(nèi)容
當(dāng)進(jìn)行 export default 時(shí),它和commonjs一樣暴露內(nèi)容的淺拷貝;
當(dāng)進(jìn)行 import { } from 時(shí),它到處內(nèi)容的引用,將會(huì)影響源文件內(nèi)容;
