- 在 JavaScript 的模塊中辛辨,首先我們可以把語(yǔ)言按照文法十气、語(yǔ)義和運(yùn)行時(shí)來(lái)拆分励背,這符合編程語(yǔ)言的一般規(guī)律:用一定的詞法和語(yǔ)法,表達(dá)一定語(yǔ)義砸西,從而操作運(yùn)行時(shí)
- 接下來(lái)叶眉,把運(yùn)行時(shí)分為「數(shù)據(jù)結(jié)構(gòu)」和「算法」
- 數(shù)據(jù)結(jié)構(gòu)包含「類型」和「實(shí)例」,JavaScript 的類型系統(tǒng)就是它的 7 種基本類型和 7 種語(yǔ)言類型芹枷,實(shí)例就是它的內(nèi)置對(duì)象部分
- 所謂的算法就是 JavaScript 的執(zhí)行過(guò)程
JavaScript 的類型
1. 根據(jù)最新的語(yǔ)言標(biāo)準(zhǔn)衅疙,7 種語(yǔ)言類型:
Undefined
Null
Boolean
String
Number
Symbol
Object
2. Undefined
Undefined
類型的值是 undefined
(declared but not defined 聲明但未賦值,同 is not defined 不同), 任何變量在賦值前是 Undefined
類型鸳慈,值為 undefined
饱溢。
由于 JS 語(yǔ)言的設(shè)計(jì)失誤,undefined
是一個(gè)變量蝶涩,而非一個(gè)關(guān)鍵字理朋。為了避免無(wú)意中被篡改絮识,建議使用 void 0
來(lái)獲取 undefined
的值绿聘。以下幾種方式都會(huì)輸出 undefined
:
console.log(void 0);
console.log(void (0));
console.log(void "hello word!");
console.log(void true);
在實(shí)際編程時(shí),我們一般不會(huì)把變量賦值為 undefined
次舌,這樣可以保證所有值為 undefined
的變量熄攘,都是從未賦值的自然狀態(tài)。
3. Null
Null
類型也只有一個(gè)值彼念,就是 null
挪圾,表示的是:“定義了但是為空”浅萧。與 undefined
不同,null
是JavaScript 的關(guān)鍵字哲思,所以在任何代碼中洼畅,都可以放心用 null
關(guān)鍵字來(lái)獲取 null
值。
4. Boolean
比較簡(jiǎn)單棚赔,有關(guān)鍵字 true
false
來(lái)表示兩個(gè)值帝簇。
5. String
String
用于表示文本數(shù)據(jù),最大長(zhǎng)度為 2^53 - 1靠益,這個(gè)所謂最大長(zhǎng)度丧肴,并不是字符數(shù)。因?yàn)?String
的意義并非“字符串”胧后,而是字符串的 UTF16 編碼芋浮。所以字符串的最大長(zhǎng)度,實(shí)際上是受字符串的編碼長(zhǎng)度影響的壳快。
6. Number
JavaScript 中有 2^64 - 2^53 + 3 個(gè)值纸巷,NaN
、Infinity
眶痰、-Infinity
都是 Number
類型何暇。
根據(jù)浮點(diǎn)數(shù)的定義,非整數(shù)的 Number
類型無(wú)法用 == (或 ===)來(lái)比較凛驮,so:
console.log( 0.1 + 0.2 == 0.3); // false
浮點(diǎn)數(shù)運(yùn)算的精度問(wèn)題導(dǎo)致等式兩邊的結(jié)果并不是嚴(yán)格相等裆站,而是相差了個(gè)微小的值。所以黔夭,需要一個(gè)正確的比較方法:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
7. Symbol
Symbol
是 ES6 引入的心類型宏胯,它是一切非字符串對(duì)象 key 的集合。
8. Object
8.1 為什么給對(duì)象添加的方法能用在基本類型上本姥?
答:.
運(yùn)算符提供了「裝箱操作」肩袍,它會(huì)根據(jù)基礎(chǔ)類型構(gòu)造一個(gè)臨時(shí)對(duì)象,使得我們能在基本類型上調(diào)用對(duì)應(yīng)對(duì)象的方法婚惫。
在 JavaScript 中氛赐,對(duì)象的定義是“屬性的集合”。屬性分為數(shù)據(jù)屬性和訪問(wèn)器屬性先舷,二者都是 key-value 結(jié)構(gòu)艰管,key 可以是字符串或者 Symbol 類型。
8.2 JavaScript 中幾個(gè)基本類型蒋川,都在對(duì)象類型中有個(gè)“親戚”牲芋,它們是:
Number
String
Boolean
Symbol
注: 3
與 new Number(3)
完全不同,前者是 Number 類型,后者是對(duì)象類型缸浦。
Number
String
Boolean
的構(gòu)造器是兩用的夕冲,當(dāng)跟 new
搭配時(shí),它們產(chǎn)生新對(duì)象裂逐;當(dāng)直接調(diào)用時(shí)歹鱼,它們表示強(qiáng)制類型轉(zhuǎn)換。
注:Symbol
函數(shù)比較特殊卜高,直接使用 new
調(diào)用它會(huì)拋出錯(cuò)誤醉冤,但它仍然是 Symbol
對(duì)象的構(gòu)造器。
8.3 JavaScript 語(yǔ)言在設(shè)計(jì)上模糊了對(duì)象和基本類型之間的關(guān)系篙悯,so 可以把對(duì)象的方法再基本類型上使用:
console.log("abc".charAt(0)); //a
甚至可以在原型鏈上添加方法蚁阳,如:
Symbol.prototype.hello = () => console.log("hello");
var a = Symbol("a");
console.log(typeof a); //symbol,a 并非對(duì)象
a.hello(); //hello鸽照,有效
類型轉(zhuǎn)換
推薦禁止使用 ==
螺捐,而要求程序員進(jìn)行顯式地類型轉(zhuǎn)換后,用 ===
比較矮燎。
常見(jiàn)轉(zhuǎn)換規(guī)則如下表:
JavaScript 中對(duì)象的特征
在實(shí)現(xiàn)了對(duì)象基本特征的基礎(chǔ)上定血,JavaScript 中對(duì)象獨(dú)有的特色是:具有高度的動(dòng)態(tài)性 -- 這是因?yàn)?JavaScript 富裕了使用者在運(yùn)行時(shí)為對(duì)象添改狀態(tài)和行為的能力。
1. JavaScript 對(duì)象的兩類屬性
1.1 數(shù)據(jù)屬性诞外,四個(gè)特征:
- value:屬性的值
- writable:決定屬性能否被賦值
- enumerable:決定 for in 能否枚舉該屬性
- configuration:決定該屬性能否被刪除或者改變特征值
大多數(shù)情況下澜沟,只關(guān)注數(shù)據(jù)屬性的「值」即可。
1.2 訪問(wèn)器屬性(getter/setter):
- getter:函數(shù)或
undefined
, 在取屬性值時(shí)被調(diào)用 - setter:函數(shù)或
undefined
, 在設(shè)置屬性值時(shí)被調(diào)用 - enumerable:決定 for in 能否枚舉該屬性
- configuration:決定該屬性能否被刪除或者改變特征值
Object.defineProperty 來(lái)定義屬性峡谊,這樣定義屬性可以改變屬性的 writable 和 enumerable
Object.getOwnPropertyDescriptor 查看屬性
2. JavaScript 中的常用對(duì)象
2.1 內(nèi)置對(duì)象(Built-in Objects)
ECMA-262 把內(nèi)置對(duì)象(built-in object)定義為“由 ECMAScript 實(shí)現(xiàn)提供的茫虽、獨(dú)立于宿主環(huán)境的所有對(duì)象,在 ECMAScript 程序開(kāi)始執(zhí)行時(shí)出現(xiàn)”既们。這意味著開(kāi)發(fā)者不必明確實(shí)例化內(nèi)置對(duì)象濒析,它已被實(shí)例化了。ECMA-262 只定義了兩個(gè)內(nèi)置對(duì)象啥纸,即 Global 和 Math (它們也是本地對(duì)象号杏,根據(jù)定義,每個(gè)內(nèi)置對(duì)象都是本地對(duì)象)
MDN 的原文解釋:
「The term "global objects" (or standard built-in objects) here is not to be confused with the global object. Here, global objects refer to objects in the global scope. The global object itself can be accessed using the this
operator in the global scope (but only if ECMAScript 5 strict mode is not used; in that case it returns undefined
). In fact, the global scope consists of the properties of the global object, including inherited properties, if any.
Other objects in the global scope are either created by the user script or provided by the host application. The host objects available in browser contexts are documented in the API reference. For more information about the distinction between the DOM and core JavaScript, see JavaScript technologies overview.
」
2.3 宿主對(duì)象(Host Objects)
“宿主”就是我們網(wǎng)頁(yè)的運(yùn)行環(huán)境斯棒,即“操作系統(tǒng)”和“瀏覽器”盾致。所有非本地對(duì)象都是宿主對(duì)象(host object),即由 ECMAScript 實(shí)現(xiàn)的宿主環(huán)境提供的對(duì)象荣暮。所有的 BOM 和 DOM 對(duì)象都是宿主對(duì)象庭惜。
因?yàn)槠鋵?duì)于不同的“宿主”環(huán)境所展示的內(nèi)容不同。也就是說(shuō)渠驼,ECMAScript 官方未定義的對(duì)象都屬于宿主對(duì)象蜈块,因?yàn)槠湮炊x的對(duì)象大多數(shù)是自己通過(guò) ECMAScript 程序創(chuàng)建的對(duì)象。
2.3.1 瀏覽器對(duì)象:
-
window
對(duì)象是最頂層的對(duì)象迷扇; -
window
對(duì)象有6大屬性百揭,包括: -
document
、frames
蜓席、history
器一、location
、navigator
厨内、screen
祈秕,這6大屬性本身也是對(duì)象; -
window
對(duì)象下的document
屬性也是對(duì)象雏胃,并且document
下也有5大屬性(anchors
请毛、forms
、images
瞭亮、links
方仿、location
)也是對(duì)象
2.3.2 JavaScript 各類型關(guān)系圖:
這張圖的含義如下:
1、內(nèi)置(Build-in)對(duì)象與原生(Naitve)對(duì)象的區(qū)別在于:前者總是在引擎初始化階段就被創(chuàng)建好的對(duì)象统翩,是后者的一個(gè)子集仙蚜;而后者包括了一些在運(yùn)行過(guò)程中動(dòng)態(tài)創(chuàng)建的對(duì)象。
2厂汗、引擎擴(kuò)展對(duì)象是一個(gè)并不太大的集合委粉,一般來(lái)說(shuō)比較確定,它們也屬于引擎的原生對(duì)象(但不屬于ECMA規(guī)范的原生對(duì)象)娶桦。
3贾节、宿主對(duì)象不是引擎的原生對(duì)象,而是由宿主框架通過(guò)某種機(jī)制注冊(cè)到 JavaScript 引擎中的對(duì)象衷畦。
4氮双、一些宿主會(huì)把自己提供的對(duì)象/構(gòu)造器也稱為“原生對(duì)象”,例如 Internet Explorer 7 就把它提供的 XMLHttpRequest() 稱為原生的——與此相對(duì)的是在它的更早先版本中通過(guò) new ActiveXObject('Microsoft.XMLHTTP') 這樣的方法創(chuàng)建的對(duì)象霎匈。這種情況下戴差,讀者應(yīng)注意到“宿主的原生對(duì)象”與“引擎的原生對(duì)象”之間的差異。
宏觀任務(wù)(Macro Tasks)和微觀任務(wù)(Micro Tasks)
宏任務(wù):script (整體代碼)铛嘱,setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering.
微任務(wù):process.nextTick, Promise.then, Object.observe, MutationObserver.
圖解1:
圖解2:
舉個(gè)例子:
setTimeout(function() {
console.log('1. setTimeout');
})
new Promise(function(resolve) {
console.log('2. promise');
resolve();
}).then(function() {
console.log('3. then');
})
console.log('4. console');
解釋:
- 這段代碼作為宏任務(wù)暖释,進(jìn)入主線程。
- 先遇到
setTimeout
墨吓,那么將其回調(diào)函數(shù)注冊(cè)后分發(fā)到宏任務(wù)Event Queue球匕。(注冊(cè)過(guò)程與上同,下文不再描述) - 接下來(lái)遇到了
Promise
帖烘,new Promise
立即執(zhí)行,then
函數(shù)分發(fā)到微任務(wù)Event Queue。 - 遇到console.log()照卦,立即執(zhí)行式矫。
- 好啦,整體代碼
script
作為第一個(gè)宏任務(wù)執(zhí)行結(jié)束役耕,看看有哪些微任務(wù)采转?我們發(fā)現(xiàn)了then
在微任務(wù) Event Queue 里面,執(zhí)行瞬痘。 - OK故慈,第一輪事件循環(huán)結(jié)束了,我們開(kāi)始第二輪循環(huán)框全,當(dāng)然要從宏任務(wù) Event Queue 開(kāi)始察绷。我們發(fā)現(xiàn)了宏任務(wù) Event Queue 中
setTimeout
對(duì)應(yīng)的回調(diào)函數(shù),立即執(zhí)行津辩。 - 結(jié)束克婶。
更多詳情,請(qǐng)參考 這一次丹泉,徹底弄懂 JavaScript 執(zhí)行機(jī)制
函數(shù)的分類
1. 普通函數(shù)
function foo(){
// code
}
2. 箭頭函數(shù)
const foo = () => {
// code
}
3. 在 class 中定義的函數(shù)
class C {
foo(){
//code
}
}
4. 生成器 generator 函數(shù):用 function* 定義的函數(shù)
function* foo(){
// code
}
5. 用 class 定義的類情萤,實(shí)際上也是函數(shù)
class Foo {
constructor(){
//code
}
}
6. 異步函數(shù):普通函數(shù)、箭頭函數(shù)摹恨、生成器函數(shù)加上 async 關(guān)鍵字:
async function foo(){
// code
}
const foo = async () => {
// code
}
async function foo*(){
// code
}
通過(guò) new 調(diào)用函數(shù)筋岛,跟直接調(diào)用 this 取值有明顯區(qū)別,以上的函數(shù)同 new 搭配如下表:
函數(shù)類型 | new |
---|---|
普通函數(shù) | 新對(duì)象 |
箭頭函數(shù) | X |
方法 | X |
生成器 | X |
類 | 新對(duì)象 |
異步普通函數(shù) | X |
異步箭頭函數(shù) | X |
異步生成器函數(shù) | X |