學(xué)習(xí)材料是winter前輩的《重學(xué)前端》課程敏沉,記錄一下盲點(diǎn)诊沪。
類型
- 為什么推薦用void 0代替undefined
undefined不是關(guān)鍵字差导,此為js公認(rèn)的設(shè)計(jì)失誤之一被啼;void接任意表達(dá)式都是undefined帜消。為了防止undefined被無意篡改,推薦用void 0來獲取undefined值浓体。
window上的undefined是無法修改的泡挺,局部變量可以修改。 - string的最大長(zhǎng)度
string并非表示其展示的字符串命浴,而是其UTF編碼娄猫,最大長(zhǎng)度為2^53 - 1。
字符是以Unicode的方式表示的生闲,每一個(gè)Unicode的碼點(diǎn)表示一個(gè)字符媳溺,UTF是Unicode的編碼方式,規(guī)定了碼點(diǎn)在計(jì)算機(jī)中的表示方法碍讯,常見的有UTF16和UTF8悬蔽,可以理解為兩套字典。
js是UTF16捉兴,所以表示為U+4位16進(jìn)制數(shù)屯阀。
絕大部分的常用字符都是4位16進(jìn)制數(shù)缅帘,也就是U+0000到U+FFFF,被稱為基本字符區(qū)域BMP难衰,這部分的碼點(diǎn)個(gè)數(shù)跟表現(xiàn)出來的字符串長(zhǎng)度是一致的。
還有剩余部分從U+010000到U+10FFFF的字符被稱為補(bǔ)充字符逗栽,也叫做代理盖袭,用兩個(gè)BMP碼點(diǎn)表示,形成一個(gè)代理對(duì)彼宠,表示一個(gè)補(bǔ)充字符鳄虱。
正常來說都是BMP,校驗(yàn)長(zhǎng)度也應(yīng)該是不需要考慮補(bǔ)充字符的凭峡。補(bǔ)充字符舉個(gè)栗子:'\uD83C\uDF03'
??拙已,長(zhǎng)度為2。
補(bǔ)充字符除了用代理對(duì)表示摧冀,還可以用String.fromCodePoint構(gòu)造倍踪,比如上面這個(gè)例子,使用0x1F303
作為參數(shù)即可索昂;\u只表示BMP建车,對(duì)應(yīng)的字符構(gòu)造方法是String.fromCharCode。
- 0.1 + 0.2 !== 0.3
這個(gè)之前在筆記里有寫椒惨,與浮點(diǎn)數(shù)的計(jì)算方式有關(guān)缤至,這個(gè)二進(jìn)制算出來是個(gè)無限循環(huán)。
正確的計(jì)算方式是Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON
康谆。 - 為什么給對(duì)象添加的方法能用在基本類型上
一開始沒太明白這句話的意思领斥,實(shí)際上說的是為什么可以像'abc'.charAt(0)
(3.2).toFixed(2)
這樣在基本類型上調(diào)用對(duì)象的方法。
原因是基本類型的.
操作符提供了裝箱操作沃暗,將基本類型轉(zhuǎn)為對(duì)象類型String月洛、Number、Boolean描睦、Symbol膊存,這些對(duì)象類型原型上的方法自然可以被調(diào)用。 - 類型轉(zhuǎn)換
==運(yùn)算是設(shè)計(jì)失誤忱叭,關(guān)于隱式轉(zhuǎn)換規(guī)則隔崎,之前有寫過一篇JS 隱式轉(zhuǎn)換。當(dāng)時(shí)還不知道拆裝箱轉(zhuǎn)換韵丑。
裝箱上面說了爵卒,就是從基本類型轉(zhuǎn)為對(duì)象類型,前三種都可以用new來得到撵彻。symbol有點(diǎn)特殊钓株,無法用new構(gòu)造实牡,使用Symbol構(gòu)造的是symbol基本類型。構(gòu)造Symbol對(duì)象類型可以用Object(Symbol('xxx'))
來得到轴合。
拆箱則是逆操作创坞,原理是對(duì)象的Symbol.toPrimitive方法,具體則是valueOf和toString受葛,兩者經(jīng)過后都未返回基本類型則拋出TypeError異常题涨。
調(diào)用valueOf和toString的順序則是取決于上下文,比如
let o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o * 2
// 因?yàn)槭撬阈g(shù)運(yùn)算总滩,所以是先valueOf再toString纲堵,最后類型異常
let o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
String(o)
// 字符串操作,先toString再valueOf闰渔,最后類型異常
拋出的異常都是Cannot convert object to primitive value
席函,要說明一下這兩個(gè)方法的返回值,如果返回基本類型冈涧,會(huì)進(jìn)行隱式轉(zhuǎn)換茂附,返回對(duì)象類型則會(huì)再進(jìn)行拆箱轉(zhuǎn)換。當(dāng)然隱式轉(zhuǎn)換本身也包括對(duì)象類型的拆箱炕舵。
另外何之,在es6之后,還允許顯示聲明對(duì)象的Symbol.toPrimitive方法來覆蓋toString和valueOf
let o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
String(o)
// 只會(huì)調(diào)用Symbol.toPrimitive咽筋,前面兩個(gè)都沒調(diào)用溶推,打印hello
-
promise async/await
之前在面試隨筆里寫過一些內(nèi)容,不完全正確
面試記錄
首先await確實(shí)就是用來修飾Promise的奸攻,當(dāng)時(shí)理解并沒錯(cuò)蒜危。
另外是generator/iterator,generator/iterator并非異步代碼睹耐,只是在缺少async/await時(shí)辐赞,co利用其特性來模擬async/await;但是generator并非被設(shè)計(jì)成實(shí)現(xiàn)異步硝训,語法糖的說法只適用于co實(shí)現(xiàn)响委。
- Completion Record類型
此為js的標(biāo)準(zhǔn)類型,用來描述語句執(zhí)行之后的結(jié)果窖梁。它有三個(gè)字段:
- [[type]] 完成的類型赘风,有break、continue纵刘、return邀窃、throw和normal幾種類型;
- [[value]] 語句的返回值假哎,沒有則為empty;
- [[target]] 語句的模板,通常是一個(gè)js標(biāo)簽呀打。
type為normal的Completion Record主要有聲明類語句、表達(dá)式語句劣砍、空語句和debugger。
在try中有return語句笋轨,finally中的內(nèi)容還會(huì)執(zhí)行嗎秆剪?
會(huì)執(zhí)行,因?yàn)閒inally中的內(nèi)容必須保證執(zhí)行爵政,所以 try/catch執(zhí)行完畢,即使得到的結(jié)果是非normal型的完成記錄陶缺,也必須要執(zhí)行finally钾挟。而當(dāng)finally執(zhí)行也得到了非normal記錄,則會(huì)使finally中的記錄作為整個(gè)try結(jié)構(gòu)的結(jié)果饱岸。
至于target掺出,這個(gè)是js比較冷門的語法,帶標(biāo)簽的語句苫费。
比如聲明 defLabel: var i = 0;
汤锨,大部分時(shí)候這個(gè)東西相當(dāng)于注釋,沒有任何作用百框,且不能聲明let和const闲礼;但可以跳出外層循環(huán)。
outer: while(true) {
inner: while(true) {
break outer;
}
}
console.log("finished")
相當(dāng)于錨點(diǎn)铐维,指定label消費(fèi)break柬泽。(continue同理)
- 為什么
12.toString()
會(huì)報(bào)錯(cuò)
因?yàn)?2后的.會(huì)被當(dāng)成小數(shù)點(diǎn),可以12 .toString()
嫁蛇,之前都是用小括號(hào)包起來的锨并。 - 空白字符
之前在可編輯的有序列表中提到過“隱式空白字符”,HTML代碼表示為​
睬棚,其正式名稱叫做零寬字符<BOM>第煮,與其類似的還有零寬連接符<ZWJ>和零寬非連接符<ZWNJ>,用Unicode表示依次為\uFEFF
\u200D
\u200C
抑党。 - 不寫行尾分號(hào)可能引起的問題
先說原理:自動(dòng)補(bǔ)分號(hào)的條件有一條是“此處有換行符包警,但語法規(guī)定此處不能有換行符”,但js中有no LineTerminator here 規(guī)則新荤,即不能插入換行揽趾。
記幾條比較少見的:
- 后自增/自減前
- async、yield
- 箭頭函數(shù)的箭頭前
回到問題苛骨,不寫分號(hào)可能引起的幾種情況:
(function(a){
console.log(a);
})()/*這里沒有被自動(dòng)插入分號(hào)*/
(function(a){
console.log(a);
})()
比較好理解篱瞎,第二個(gè)自執(zhí)行當(dāng)成了上面的鏈?zhǔn)秸{(diào)用參數(shù)苟呐。
var a = [[]]/*這里沒有被自動(dòng)插入分號(hào)*/
[3, 2, 1, 0].forEach(e => console.log(e))
說明一下,這里能連上而且不會(huì)報(bào)錯(cuò)俐筋。
會(huì)看做是數(shù)組用中括號(hào)取值牵素,3,2,1,0當(dāng)做表達(dá)式,返回0澄者,結(jié)果就是[].forEach笆呆,不會(huì)報(bào)錯(cuò)。
var x = 1, g = {test:()=>0}, b = 1/*這里沒有被自動(dòng)插入分號(hào)*/
/(a)/g.test("abc")
console.log(RegExp.$1)
正則的/當(dāng)成了除號(hào)粱挡。
var f = function(){
return "";
}
var g = f/*這里沒有被自動(dòng)插入分號(hào)*/
`Template`.match(/(a)/);
console.log(RegExp.$1)
模板字符串特性赠幕,可以跟在函數(shù)后面當(dāng)成參數(shù)。
這些都是不滿足no LineTerminator here 規(guī)則的询筏,所以不會(huì)在行尾加分號(hào)榕堰。
- 預(yù)處理
var變量提升的原理,有段反常識(shí)的代碼
var a = 1;
function foo() {
var o= {a:3}
with(o) {
var a = 2;
}
console.log(o.a);
console.log(a);
}
foo();
foo內(nèi)嫌套,a聲明提到首行逆屡,with(o)修改a時(shí)不影響外部a的賦值,所以o.a為2踱讨,a為undefined魏蔗。
- 指令序言(Directive Prologs)
js中規(guī)定的唯一指令序言是"use strict";
- 乘方表達(dá)式
-2 ** 2會(huì)報(bào)錯(cuò),一元表達(dá)式不能放入乘方表達(dá)式痹筛;乘方表達(dá)式是右結(jié)合的莺治。 - 為什么沒有“父選擇器”
因?yàn)闉g覽器構(gòu)建DOM樹的順序是父-子,當(dāng)解析到一個(gè)dom時(shí)就應(yīng)該完全得到其樣式信息味混,而不是還要依賴未被構(gòu)建的子節(jié)點(diǎn)來判斷是否選中产雹,所以不會(huì)出現(xiàn)“父選擇器”。