文章引用來源
極客時間版權(quán)所有: https://time.geekbang.org/column/article/78884
JavaScript中的每一個值都有它自己的類型床玻,JavaScript規(guī)定了七種語言類型劳景,他們是:
- Undefined
- Null
- Boolean
- String
- Number
- Symbol
- Object
我們來談?wù)勥@些類型的一部分關(guān)鍵知識點
Undefined和Null
思考這個問題:為什么有的編程規(guī)范要求用 void 0 代替 undefined?
undefined
- undefined類型表示未定義坑赡,它的值只有一個:undefined
- 任何變量賦值前都是undefined類型奄妨,值為undefined(而不是null)
- undefined是一個變量涂籽,而非一個關(guān)鍵字
- 需要表達這個值,可以用全局變量undefined砸抛,或者void運算
我們針對第三點來展開討論评雌,得出上述問題的答案。
undefined是一個變量直焙,我們避免無意中被篡改景东,建議使用void 0來獲取undefined(等價于void (0) )。我們一般不會把變量賦值為undefined奔誓,這樣可以保證所有的值為undefined的變量斤吐,都是從未賦值的自然狀態(tài)。
void運算
語法:void express 或者 void (express)
void運算符所做的是厨喂,執(zhí)行表達式express和措,然后不論表達式是否有返回值,一律返回undefined蜕煌。
void運算符如果使用括號派阱,括號內(nèi)必須有表達式,如果是void()會被視為執(zhí)行名為void的函數(shù)斜纪,報錯:SyntaxError贫母。
死鏈接
我們有時候會用href="#"來表示死鏈接,但是這樣會導致頁面跳到最上面的視圖盒刚,#包含了一個位置信息腺劣,默認的錨是#top,也就是網(wǎng)頁的上端因块。在頁面很長的時候會使用#來定位頁面的具體位置橘原,格式為:#+id
href=javascript:void(0)用來禁止a標簽的跳轉(zhuǎn)行為,javascript:
是偽協(xié)議贮聂,表示url的內(nèi)容通過JavaScript執(zhí)行靠柑,void(0)
表示不作任何操作寨辩。
<a href="javascript: void(0)" onClick="window.open()"><!--點擊鏈接后頁面不動吓懈,只打開鏈接-->
<a href="#" onClick="javascript: return false"><!--作用一樣-->
Null
- 只有一個值,就是null
- 表示空值靡狞,是關(guān)鍵字耻警,可以放心使用null關(guān)鍵字來獲取null值。
String
- string的意義并非“字符串”,而是字符串的UTF16編碼甘穿,字符串的最大長度實際上是受字符串編碼長度影響的腮恩。最大長度:253 - 1 。
- 字符串是永遠無法變更的温兼,一旦字符串被構(gòu)造出來秸滴,無法用任何方式改變字符串的內(nèi)容。
- 字符串把每個UTF16單元當做一個字符來處理募判,所以處理非BMP(超出 U+0000 - U+FFFF 范圍)的字符時荡含,應(yīng)該格外小心。這個設(shè)計繼承自Java届垫,現(xiàn)實中很少用到BMP之外的字符释液。
Number
思考下面問題:為什么在 JavaScript 中,0.1+0.2 不等于0.3
- number類型有264- 253+3 個值装处。
- 基本符合 IEEE 754-2008 規(guī)定的雙精度浮點數(shù)規(guī)則误债,但也有額外幾個表達的語言場景(比如不讓除以0出錯诗茎,引入了無窮大)逸嘀。
- NaN阅茶,占用了 9007199254740990未巫,這原本是符合 IEEE 規(guī)則的數(shù)字
- Infinity阱驾,無窮大
- -Infinity臀稚,負無窮大
- 有+0和-0吊趾,加法運算中沒有區(qū)別彩库,但是除法要區(qū)分形帮,“忘記檢測除以 -0槽惫,而得到負無窮大”的情況經(jīng)常會導致錯誤,而區(qū)分 +0 和 -0 的方式辩撑,正是檢測 1/x 是 Infinity還是-Infinity
- 根據(jù)雙精度浮點數(shù)定義界斜,有效的整數(shù)范圍是 -0x1fffffffffffff 至 0x1fffffffffffff,無法精確表示此范圍外的整數(shù)合冀。
- 根據(jù)雙精度浮點數(shù)定義各薇,非整數(shù)的Number類型無法用==來比較(三個等號也不行),正確的比較方法是用JavaScript提供的最小精度值:
console.log( 0.1 + 0.2 == 0.3);//false
//正確的比較方法
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);//true
Symbol
- 表示獨一無二的值君躺,它是一切非字符串的對象key的集合峭判。
- Symbol 值通過Symbol函數(shù)生成。這就是說棕叫,對象的屬性名現(xiàn)在可以有兩種類型林螃,一種是原來就有的字符串,另一種就是新增的 Symbol 類型俺泣。凡是屬性名屬于 Symbol 類型疗认,就都是獨一無二的完残,可以保證不會與其他屬性名產(chǎn)生沖突。
Symbol函數(shù)可以接受一個字符串作為參數(shù)横漏,表示對 Symbol 實例的描述谨设,但是即使描述相同,Symbol值也不相等缎浇。
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false
- 一些標準中提到的 Symbol扎拣,可以在全局的 Symbol 函數(shù)的屬性中找到。例如素跺,我們可以使用 Symbol.iterator 來自定義 for…of 在對象上的行為:
var o = new Object
o[Symbol.iterator] = function() {
var v = 0
return {
next: function() {
return { value: v++, done: v > 10 }
}
}
};
for(var v of o)
console.log(v); // 0 1 2 3 ... 9
Object
問題:為什么給對象添加的方法能用在基本類型上鹏秋?
- JavaScript對象的定義是“屬性的集合”。屬性分為數(shù)據(jù)屬性和訪問器屬性亡笑,二者都是 key-value 結(jié)構(gòu)侣夷,key可以是字符串或者Symbol類型。
- JavaScript的“類”僅僅是運行時對象的一個私有屬性仑乌,而JavaScript中是無法自定義類型的百拓。
- Number、String 和 Boolean晰甚,三個構(gòu)造器是兩用的衙传,當跟 new 搭配時,它們產(chǎn)生對象厕九,當直接調(diào)用時蓖捶,它們表示強制類型轉(zhuǎn)換。
- Symbol 函數(shù)比較特殊扁远,直接用 new 調(diào)用它會拋出錯誤俊鱼,但它仍然是Symbol對象的構(gòu)造器。
- 我們在原型上添加方法畅买,都可以應(yīng)用于基本類型并闲。
Symbol.prototype.hello = () => console.log("hello");
var a = Symbol("a");
console.log(typeof a); //symbol,a 并非對象
a.hello(); //hello谷羞,有效
上述問題的答案是:運算符提供了裝箱操作帝火,它會根據(jù)基礎(chǔ)類型構(gòu)造一個臨時對象,使得能夠在基礎(chǔ)類型上調(diào)用對應(yīng)對象的方法湃缎。
裝箱和拆箱
裝箱轉(zhuǎn)換:基本類型-->對象
- 每一種基本類型犀填,都在對象中有對應(yīng)的類,裝箱機制會頻繁產(chǎn)生臨時對象嗓违。
- 使用object函數(shù)九巡,可以顯示調(diào)用裝箱能力。
- 每一類裝箱對象皆有私有的 Class 屬性靠瞎,這些屬性可以O(shè)bject.prototype.toString 獲取比庄。在 JavaScript 中求妹,沒有任何方法可以更改私有的Class 屬性乏盐,因此 Object.prototype.toString 是可以準確識別對象對應(yīng)的基本類型的方法佳窑,它比 instanceof 更加準確。
- call函數(shù)本身會產(chǎn)生裝箱操作父能,需要配合typeof來區(qū)分基本類型還是對象類型神凑。
拆箱轉(zhuǎn)換:對象-->基本類型
- ToPrimitive 函數(shù),它是對象類型到基本類型的轉(zhuǎn)換
- 拆箱轉(zhuǎn)換會嘗試調(diào)用 valueOf 和 toString 來獲得拆箱后的基本類型何吝。如果valueOf 和 toString都不存在溉委,或者沒有返回基本類型,則會產(chǎn)生TypeError爱榕。
對象的運算
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o * 2
// valueOf
// toString
// TypeError
String的拆箱
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o + ""
// toString
// valueOf
// TypeError