一泛鸟,數(shù)據(jù)類(lèi)型
1蝠咆,基本數(shù)據(jù)類(lèi)型(值類(lèi)型):
number,string谈况,boolean勺美,null,undefined碑韵。
2,引用數(shù)據(jù)類(lèi)型:
對(duì)象缎脾,數(shù)組祝闻,函數(shù)。
如:{ }遗菠,[ ]联喘, 日期對(duì)象new Date(), Math辙纬,實(shí)例對(duì)象...
3豁遭,ES6中新增,Symbol(唯一值)
4贺拣,ES11(ES2020)新增蓖谢,bigInt(一種方法來(lái)表示大于 2?3-1 的整數(shù)捂蕴,BigInt 可用于任意大整數(shù))。
例如:let a = Symbol('1')
? ? ? ? ? let b = Symbol('1')
? ? ? ? ? a != b? // true
另:NaN == NaN? // false闪幽,NaN和誰(shuí)都不相等啥辨。
二,變量
創(chuàng)建一個(gè)變量會(huì)有三步操作
例如:let a = 1
1盯腌,創(chuàng)建變量:聲明 let a
2溉知,創(chuàng)建值: 基本值直接在棧中創(chuàng)建和存儲(chǔ)? 1
3,讓變量和值關(guān)聯(lián)起來(lái)(賦值):定義 defined腕够。當(dāng)然未賦值是undefined(ps:終于知道undefined怎么來(lái)的了)级乍。
在第2步中,當(dāng)引用值是復(fù)雜的結(jié)構(gòu)時(shí)特殊處理帚湘。
如:let obj = { a: 1 }
function Fn(){ let a = 1 }
(ps:函數(shù)也是變量卡者,和let創(chuàng)建的變量本質(zhì)上是一樣的,區(qū)別是存儲(chǔ)的值是個(gè)函數(shù)類(lèi)型的值客们。)
1崇决,開(kāi)辟一個(gè)存儲(chǔ)對(duì)象中鍵值對(duì)(存儲(chǔ)函數(shù)中的代碼)的內(nèi)存空間“堆內(nèi)存”。
2底挫,所有的堆內(nèi)存都有一個(gè)可被后續(xù)查找的16進(jìn)制地址恒傻。
3,后續(xù)關(guān)聯(lián)賦值的時(shí)候建邓,是把堆內(nèi)存地址給與變量操作盈厘。
如下圖所示存儲(chǔ):
問(wèn)題:
1,let 和 var的區(qū)別官边?
let 語(yǔ)句創(chuàng)建了自己的作用域沸手,這個(gè)作用域里的變量與外面的變量無(wú)關(guān)。
2注簿,變量提升是怎么回事契吉?
JavaScript會(huì)把作用域里的所有變量和函數(shù)提到函數(shù)的頂部聲明,帶著這個(gè)原理看以下的例子诡渴。
上圖中的值是undefined說(shuō)明變量已經(jīng)提前聲明捐晶,只是沒(méi)有被賦值。由此解釋?zhuān)兞刻嵘傅氖亲兞柯暶鞯奶嵘纾粫?huì)提升變量的初始化和賦值惑灵。
let的情況不同:
瀏覽器認(rèn)為c沒(méi)有聲明,是否意味著沒(méi)有變量提升呢眼耀?再看一個(gè)例子英支,
這里同樣認(rèn)為c沒(méi)有聲明,但是哮伟,如果c沒(méi)有變量提升干花,執(zhí)行到console.log時(shí)應(yīng)該是輸出全局作用域中的c妄帘,而不是出現(xiàn)錯(cuò)誤。
可以推知let也出現(xiàn)了變量提升把敢。出現(xiàn)這種情況我們稱(chēng)為“暫時(shí)性死區(qū)”(temporal dead zone寄摆,簡(jiǎn)稱(chēng) TDZ),在代碼塊內(nèi)修赞,使用let婶恼、const命令聲明變量之前,該變量都是不可用的柏副。
3勾邦,函數(shù)會(huì)被優(yōu)先提升到上面
4,語(yǔ)句塊是沒(méi)有局部作用域的割择,在下面的語(yǔ)句塊中眷篇,與外部是完全一致的。if里面的變量和函數(shù)也會(huì)被提升到函數(shù)頂部
三荔泳,js引擎是怎么執(zhí)行代碼的
js引擎想要執(zhí)行代碼蕉饼,一定會(huì)創(chuàng)建一個(gè)執(zhí)行棧ECStack(執(zhí)行上下文環(huán)境棧)。
棧內(nèi)存玛歌,我們簡(jiǎn)單的理解為執(zhí)行代碼所用昧港,那么函數(shù)執(zhí)行就是個(gè)進(jìn)棧和出棧的過(guò)程。
步驟如下:
1支子,把創(chuàng)建的上下文壓縮到棧中執(zhí)行 => 進(jìn)棧
2创肥,執(zhí)行完有的上下文就沒(méi)用了 => 出棧
3,有的還要用值朋,會(huì)把它壓縮到棧底 => 閉包
我們執(zhí)行下面函數(shù)分析其過(guò)程:
四叹侄,閉包的理解
《JavaScript高級(jí)程序設(shè)計(jì)》里面對(duì)閉包的定義是,閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)昨登。對(duì)定義中的另一個(gè)函數(shù)作用域理解可能有點(diǎn)模糊趾代。
而上面三中簡(jiǎn)單的說(shuō)了下當(dāng)函數(shù)進(jìn)棧執(zhí)行后,還要用到該函數(shù)不能出棧篙骡,就形成閉包稽坤。結(jié)合《JavaScript高級(jí)程序設(shè)計(jì)》分析閉包,如圖:
五糯俗,關(guān)于作用域
創(chuàng)建一個(gè)函數(shù)的時(shí)候,會(huì)創(chuàng)建一個(gè)堆睦擂,存儲(chǔ)代碼字符串和對(duì)應(yīng)的鍵值對(duì)得湘,初始化當(dāng)前函數(shù)的作用域[[scope]] = 所在上下文EC中的變量對(duì)象VO/AO。
執(zhí)行函數(shù)的時(shí)候顿仇,會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文EC淘正,初始化this的指向摆马,初始化作用域鏈[[scopeChain]]:<AO(A), VO(G)>,創(chuàng)建AO變量對(duì)象來(lái)存儲(chǔ)變量鸿吆。(對(duì)比上圖四)
六囤采,this執(zhí)行主體的簡(jiǎn)單判斷
第一種,函數(shù)執(zhí)行惩淳,看前面是否有“點(diǎn)”蕉毯,有點(diǎn)前面是誰(shuí)this就是誰(shuí) 如:obj.fn()? ?this->obj, obj._proto_.fn? this->obj._proto
沒(méi)有點(diǎn)思犁,this是window(嚴(yán)格模式下是undefined)? 相當(dāng)window.fn()? ?this->window
自執(zhí)行函數(shù)一般this都是window代虾。
第二種,給元素的事件行為綁定方法(DOM0/DOM2)激蹲,事件觸發(fā)棉磨,方法會(huì)執(zhí)行,此時(shí)方法中的this一般都是當(dāng)前元素本身学辱,如:
? box.onclick = function() {
this-> box
? }
? box.addEventListener("click", function(){
this->box
? })
七乘瓤,原型和原型鏈
原型(prototype),每個(gè)函數(shù)都有一個(gè)prototype屬性策泣,他默認(rèn)指向一個(gè)Object空對(duì)象衙傀,原形對(duì)象有個(gè)constructor, 他指向函數(shù)對(duì)象着降。
我們給原型對(duì)象添加屬性(一般是方法)時(shí)差油,函數(shù)的所有實(shí)例對(duì)象會(huì)自動(dòng)擁有原型中的屬性。
例如:
function A() {}? ? //? 內(nèi)部語(yǔ)句:this.prototype = {}
A.prototype.test= function() {
? console.log(test)
}
let a? = new A()? //? 內(nèi)部語(yǔ)句:this(fn).__proto__ = Fn.prototype
a.test()? // test
原型有顯式原型和隱式原型任洞。
每個(gè)函數(shù)都有個(gè)prototype蓄喇,即顯示原型。在定義函數(shù)時(shí)自動(dòng)添加交掏,默認(rèn)值是一個(gè)空Object對(duì)象妆偏。
每個(gè)實(shí)例對(duì)象都有一個(gè)__proto__,稱(chēng)隱式原型盅弛。在創(chuàng)建對(duì)象時(shí)自動(dòng)添加钱骂,默認(rèn)值是構(gòu)造函數(shù)的prototype屬性。函數(shù)也是對(duì)象挪鹏,故也創(chuàng)建了__proto__见秽,下圖所示
對(duì)象的隱式原型的值為其對(duì)應(yīng)構(gòu)造函數(shù)的顯示原型的值,fn.__proto__ === Fn.prototype
在ES6之前讨盒,可以直接操作顯式原型解取,但不能直接操作隱式原型。
原型鏈返顺,訪問(wèn)一個(gè)對(duì)象時(shí)禀苦,先在自身屬性中查找蔓肯,找到返回。如果沒(méi)找到振乏,沿__proto__這條鏈向上查找蔗包,找到返回。如果最終沒(méi)找到慧邮,返回undefined调限。所以別名又叫隱式原型鏈 。
作用:查找對(duì)象的屬性和方法(備注:讀取對(duì)象屬性值是赋咽,會(huì)自動(dòng)查找原型鏈旧噪;如果是設(shè)置對(duì)象屬性時(shí),不會(huì)查找原型鏈)
我們執(zhí)行下面函數(shù)分析其查找過(guò)程:(備注:創(chuàng)建對(duì)象 let obj = {}相當(dāng) new Object()脓匿,function Fn(){}相當(dāng) Fn = new Function() )
理解上圖查找后可以對(duì)照下面的原型鏈萬(wàn)能表加深理解:
八淘钟,js事件循環(huán)
先引進(jìn)概念:
1,js是單線程的陪毡,也就是說(shuō)米母,同一個(gè)時(shí)間只能做一件事。(ps:為啥是單線程毡琉?假定JavaScript同時(shí)有兩個(gè)線程铁瞒,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn)桅滋,這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn))慧耍。
2,瀏覽器是多線程的:
3丐谋,setTimeout的執(zhí)行:setTimeout在棧中執(zhí)行后觸發(fā)瀏覽器的定時(shí)觸發(fā)器線程芍碧,定時(shí)器觸發(fā)線程倒計(jì)時(shí)在setTimeout設(shè)置的時(shí)間(如300ms)后把回調(diào)函數(shù)里面的數(shù)據(jù)加到消息隊(duì)列中。
4号俐,回調(diào)函數(shù)的執(zhí)行:回調(diào)函數(shù)會(huì)加到微任務(wù)隊(duì)列中等待執(zhí)行泌豆。如:promise執(zhí)行后會(huì)把then的回調(diào)函數(shù)加到微任務(wù)隊(duì)列中。
5吏饿,先執(zhí)行宏任務(wù)踪危,在執(zhí)行微任務(wù)隊(duì)列,最后執(zhí)行消息隊(duì)列猪落。
js事件循環(huán)機(jī)制解決單線程阻塞問(wèn)題贞远。那js是怎么處理異步(setTimeout)和回調(diào)函數(shù)(promise.then(()=>{...}))的呢?以下面代碼例子:
笨忌。兴革。。如理解有誤望指點(diǎn)學(xué)習(xí)蜜唾。