今年來(lái)臭埋,各大公司都縮減了HC踪央,甚至是采取了“裁員”措施,在這樣的大環(huán)境之下瓢阴,想要獲得一份更好的工作畅蹂,必然需要付出更多的努力。
本文挑選了20道大廠面試題荣恐,建議在閱讀時(shí)液斜,先思考一番,不要直接看解析。盡管旗唁,本文所有的答案畦浓,都是我在翻閱各種資料,思考并驗(yàn)證之后检疫,才給出的讶请。但因水平有限,本人的答案未必是最優(yōu)的屎媳,如果您有更好的答案夺溢,歡迎給我留言。如果有錯(cuò)誤烛谊,也請(qǐng)?jiān)谠u(píng)論區(qū)指出风响,謝謝。
1. new的實(shí)現(xiàn)原理是什么丹禀?
new
的實(shí)現(xiàn)原理:
- 創(chuàng)建一個(gè)空對(duì)象状勤,構(gòu)造函數(shù)中的this指向這個(gè)空對(duì)象
- 這個(gè)新對(duì)象被執(zhí)行 [[原型]] 連接
- 執(zhí)行構(gòu)造函數(shù)方法,屬性和方法被添加到this引用的對(duì)象中
- 如果構(gòu)造函數(shù)中沒(méi)有返回其它對(duì)象双泪,那么返回this持搜,即創(chuàng)建的這個(gè)的新對(duì)象,否則焙矛,返回構(gòu)造函數(shù)中返回的對(duì)象葫盼。
2. 如何正確判斷this的指向?
如果用一句話說(shuō)明 this 的指向村斟,那么即是: 誰(shuí)調(diào)用它贫导,this 就指向誰(shuí)。
但是僅通過(guò)這句話蟆盹,我們很多時(shí)候并不能準(zhǔn)確判斷 this 的指向孩灯。因此我們需要借助一些規(guī)則去幫助自己:
this 的指向可以按照以下順序判斷:
全局環(huán)境中的 this
瀏覽器環(huán)境:無(wú)論是否在嚴(yán)格模式下,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)this 都指向全局對(duì)象 window
;
node 環(huán)境:無(wú)論是否在嚴(yán)格模式下日缨,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)钱反,this 都是空對(duì)象 {}
;
是否是 new
綁定
如果是 new
綁定掖看,并且構(gòu)造函數(shù)中沒(méi)有返回 function 或者是 object匣距,那么 this 指向這個(gè)新對(duì)象。如下:
構(gòu)造函數(shù)返回值不是 function 或 object哎壳。
new Super()
返回的是 this 對(duì)象毅待。
構(gòu)造函數(shù)返回值是 function 或 object,
new Super()
是返回的是Super種返回的對(duì)象归榕。
函數(shù)是否通過(guò) call,apply 調(diào)用尸红,或者使用了 bind 綁定,如果是,那么this綁定的就是指定的對(duì)象【歸結(jié)為顯式綁定】外里。
這里同樣需要注意一種特殊情況怎爵,如果 call,apply 或者 bind 傳入的第一個(gè)參數(shù)值是 undefined
或者 null
,嚴(yán)格模式下 this 的值為傳入的值 null /undefined盅蝗。非嚴(yán)格模式下鳖链,實(shí)際應(yīng)用的默認(rèn)綁定規(guī)則,this 指向全局對(duì)象(node環(huán)境為global墩莫,瀏覽器環(huán)境為window)
隱式綁定芙委,函數(shù)的調(diào)用是在某個(gè)對(duì)象上觸發(fā)的,即調(diào)用位置上存在上下文對(duì)象狂秦。典型的隱式調(diào)用為: xxx.fn()
默認(rèn)綁定灌侣,在不能應(yīng)用其它綁定規(guī)則時(shí)使用的默認(rèn)規(guī)則,通常是獨(dú)立函數(shù)調(diào)用裂问。
非嚴(yán)格模式: node環(huán)境侧啼,執(zhí)行全局對(duì)象 global,瀏覽器環(huán)境堪簿,執(zhí)行全局對(duì)象 window慨菱。
嚴(yán)格模式:執(zhí)行 undefined
箭頭函數(shù)的情況:
箭頭函數(shù)沒(méi)有自己的this,繼承外層上下文綁定的this戴甩。
3. 深拷貝和淺拷貝的區(qū)別是什么符喝?實(shí)現(xiàn)一個(gè)深拷貝
深拷貝和淺拷貝是針對(duì)復(fù)雜數(shù)據(jù)類(lèi)型來(lái)說(shuō)的,淺拷貝只拷貝一層甜孤,而深拷貝是層層拷貝协饲。
深拷貝
深拷貝復(fù)制變量值,對(duì)于非基本類(lèi)型的變量缴川,則遞歸至基本類(lèi)型變量后茉稠,再?gòu)?fù)制。 深拷貝后的對(duì)象與原來(lái)的對(duì)象是完全隔離的把夸,互不影響而线,對(duì)一個(gè)對(duì)象的修改并不會(huì)影響另一個(gè)對(duì)象。
淺拷貝
淺拷貝是會(huì)將對(duì)象的每個(gè)屬性進(jìn)行依次復(fù)制恋日,但是當(dāng)對(duì)象的屬性值是引用類(lèi)型時(shí)膀篮,實(shí)質(zhì)復(fù)制的是其引用,當(dāng)引用指向的值改變時(shí)也會(huì)跟著變化岂膳。
可以使用 for in
誓竿、 Object.assign
、 擴(kuò)展運(yùn)算符 ...
谈截、Array.prototype.slice()
筷屡、Array.prototype.concat()
等涧偷,例如:
可以看出淺拷貝只最第一層屬性進(jìn)行了拷貝,當(dāng)?shù)谝粚拥膶傩灾凳腔緮?shù)據(jù)類(lèi)型時(shí)毙死,新的對(duì)象和原對(duì)象互不影響燎潮,但是如果第一層的屬性值是復(fù)雜數(shù)據(jù)類(lèi)型,那么新對(duì)象和原對(duì)象的屬性值其指向的是同一塊內(nèi)存地址扼倘。
深拷貝實(shí)現(xiàn)
1.深拷貝最簡(jiǎn)單的實(shí)現(xiàn)是:
JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))
是最簡(jiǎn)單的實(shí)現(xiàn)方式跟啤,但是有一些缺陷:
- 對(duì)象的屬性值是函數(shù)時(shí),無(wú)法拷貝唉锌。
- 原型鏈上的屬性無(wú)法拷貝
- 不能正確的處理 Date 類(lèi)型的數(shù)據(jù)
- 不能處理 RegExp
- 會(huì)忽略 symbol
- 會(huì)忽略 undefined
2.實(shí)現(xiàn)一個(gè) deepClone 函數(shù)
- 如果是基本數(shù)據(jù)類(lèi)型隅肥,直接返回
- 如果是
RegExp
或者Date
類(lèi)型,返回對(duì)應(yīng)類(lèi)型 - 如果是復(fù)雜數(shù)據(jù)類(lèi)型袄简,遞歸腥放。
-
考慮循環(huán)引用的問(wèn)題
4. call/apply 的實(shí)現(xiàn)原理是什么?
call
和 apply
的功能相同,都是改變 this
的執(zhí)行,并立即執(zhí)行函數(shù)雹有。區(qū)別在于傳參方式不同稽煤。
func.call(thisArg, arg1, arg2, ...)
:第一個(gè)參數(shù)是this
指向的對(duì)象懂更,其它參數(shù)依次傳入。func.apply(thisArg, [argsArray])
:第一個(gè)參數(shù)是this
指向的對(duì)象,第二個(gè)參數(shù)是數(shù)組或類(lèi)數(shù)組。
一起思考一下聚请,如何模擬實(shí)現(xiàn) call
?
首先稳其,我們知道驶赏,函數(shù)都可以調(diào)用 call
,說(shuō)明 call
是函數(shù)原型上的方法既鞠,所有的實(shí)例都可以調(diào)用煤傍。即: Function.prototype.call
。
- 在
call
方法中獲取調(diào)用call()
函數(shù) - 如果第一個(gè)參數(shù)沒(méi)有傳入嘱蛋,那么默認(rèn)指向
window / global
(非嚴(yán)格模式) - 傳入
call
的第一個(gè)參數(shù)是 this 指向的對(duì)象蚯姆,根據(jù)隱式綁定的規(guī)則,我們知道obj.foo()
,foo()
中的this
指向obj
;因此我們可以這樣調(diào)用函數(shù)thisArgs.func(...args)
- 返回執(zhí)行結(jié)果
apply
的實(shí)現(xiàn)思路和 call
一致洒敏,僅參數(shù)處理略有差別龄恋。如下:
5. 柯里化函數(shù)實(shí)現(xiàn)
在開(kāi)始之前,我們首先需要搞清楚函數(shù)柯里化的概念桐玻。
函數(shù)柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù)篙挽,并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)荆萤。
函數(shù)柯里化的主要作用:
- 參數(shù)復(fù)用
- 提前返回 – 返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)
- 延遲執(zhí)行 – 返回新函數(shù)镊靴,等待執(zhí)行
6. 如何讓 (a == 1 && a == 2 && a == 3) 的值為true铣卡?
- 利用隱式類(lèi)型轉(zhuǎn)換
==
操作符在左右數(shù)據(jù)類(lèi)型不一致時(shí),會(huì)先進(jìn)行隱式轉(zhuǎn)換偏竟。
a == 1 && a == 2 && a == 3
的值意味著其不可能是基本數(shù)據(jù)類(lèi)型煮落。因?yàn)槿绻?a 是 null 或者是 undefined bool類(lèi)型,都不可能返回true踊谋。
因此可以推測(cè) a 是復(fù)雜數(shù)據(jù)類(lèi)型蝉仇,JS 中復(fù)雜數(shù)據(jù)類(lèi)型只有 object
,回憶一下殖蚕,Object 轉(zhuǎn)換為原始類(lèi)型會(huì)調(diào)用什么方法轿衔?
如果部署了
[Symbol.toPrimitive]
接口,那么調(diào)用此接口睦疫,若返回的不是基本數(shù)據(jù)類(lèi)型害驹,拋出錯(cuò)誤。-
如果沒(méi)有部署
[Symbol.toPrimitive]
接口蛤育,那么根據(jù)要轉(zhuǎn)換的類(lèi)型宛官,先調(diào)用valueOf
/toString
- 非Date類(lèi)型對(duì)象,
hint
是default
時(shí)瓦糕,調(diào)用順序?yàn)椋?code>valueOf >>>toString
底洗,即valueOf
返回的不是基本數(shù)據(jù)類(lèi)型,才會(huì)繼續(xù)調(diào)用valueOf
咕娄,如果toString
返回的還不是基本數(shù)據(jù)類(lèi)型亥揖,那么拋出錯(cuò)誤。 - 如果
hint
是string
(Date對(duì)象的hint默認(rèn)是string) 圣勒,調(diào)用順序?yàn)椋?code>toString >>>valueOf
徐块,即toString
返回的不是基本數(shù)據(jù)類(lèi)型,才會(huì)繼續(xù)調(diào)用valueOf
灾而,如果valueOf
返回的還不是基本數(shù)據(jù)類(lèi)型胡控,那么拋出錯(cuò)誤。 - 如果
hint
是number
旁趟,調(diào)用順序?yàn)椋?valueOf
>>>toString
- 非Date類(lèi)型對(duì)象,
- 利用數(shù)據(jù)劫持(Proxy/Object.defineProperty)
- 數(shù)組的
toString
接口默認(rèn)調(diào)用數(shù)組的join
方法昼激,重寫(xiě)join
方法
7. 什么是BFC?BFC的布局規(guī)則是什么锡搜?如何創(chuàng)建BFC橙困?
Box 是 CSS 布局的對(duì)象和基本單位,頁(yè)面是由若干個(gè)Box組成的耕餐。
元素的類(lèi)型 和 display
屬性凡傅,決定了這個(gè) Box 的類(lèi)型。不同類(lèi)型的 Box 會(huì)參與不同的 Formatting Context肠缔。
Formatting Context
Formatting Context 是頁(yè)面的一塊渲染區(qū)域夏跷,并且有一套渲染規(guī)則哼转,決定了其子元素將如何定位,以及和其它元素的關(guān)系和相互作用槽华。
Formatting Context 有 BFC (Block formatting context)壹蔓,IFC (Inline formatting context),F(xiàn)FC (Flex formatting context) 和 GFC (Grid formatting context)猫态。FFC 和 GFC 為 CC3 中新增佣蓉。
- BFC內(nèi),盒子依次垂直排列亲雪。
- BFC內(nèi)勇凭,兩個(gè)盒子的垂直距離由
margin
屬性決定。屬于同一個(gè)BFC的兩個(gè)相鄰Box的margin會(huì)發(fā)生重疊【符合合并原則的margin合并后是使用大的margin】 - BFC內(nèi)义辕,每個(gè)盒子的左外邊緣接觸內(nèi)部盒子的左邊緣(對(duì)于從右到左的格式套像,右邊緣接觸)。即使在存在浮動(dòng)的情況下也是如此终息。除非創(chuàng)建新的BFC夺巩。
- BFC的區(qū)域不會(huì)與float box重疊。
- BFC就是頁(yè)面上的一個(gè)隔離的獨(dú)立容器周崭,容器里面的子元素不會(huì)影響到外面的元素柳譬。反之也如此。
- 計(jì)算BFC的高度時(shí)续镇,浮動(dòng)元素也參與計(jì)算美澳。
如何創(chuàng)建BFC
- 根元素
- 浮動(dòng)元素(float 屬性不為 none)
- position 為 absolute 或 fixed
- overflow 不為 visible 的塊元素
- display 為 inline-block, table-cell, table-caption
BFC 的應(yīng)用
- 防止 margin 重疊 (同一個(gè)BFC內(nèi)的兩個(gè)兩個(gè)相鄰Box的
margin
會(huì)發(fā)生重疊,觸發(fā)生成兩個(gè)BFC摸航,即不會(huì)重疊)- 清除內(nèi)部浮動(dòng) (創(chuàng)建一個(gè)新的 BFC制跟,因?yàn)楦鶕?jù) BFC 的規(guī)則,計(jì)算 BFC 的高度時(shí)酱虎,浮動(dòng)元素也參與計(jì)算)
- 自適應(yīng)多欄布局 (BFC的區(qū)域不會(huì)與float box重疊雨膨。因此,可以觸發(fā)生成一個(gè)新的BFC)
8. 異步加載JS腳本的方式有哪些读串?
<script>
標(biāo)簽中增加async
(html5) 或者defer
(html4) 屬性,腳本就會(huì)異步加載聊记。
<script src="../XXX.js" defer></script>
defer
和 async
的區(qū)別在于:
-
defer
要等到整個(gè)頁(yè)面在內(nèi)存中正常渲染結(jié)束(DOM 結(jié)構(gòu)完全生成,以及其他腳本執(zhí)行完成)恢暖,在window.onload 之前執(zhí)行排监; -
async
一旦下載完,渲染引擎就會(huì)中斷渲染杰捂,執(zhí)行這個(gè)腳本以后舆床,再繼續(xù)渲染。 - 如果有多個(gè)
defer
腳本,會(huì)按照它們?cè)陧?yè)面出現(xiàn)的順序加載 - 多個(gè)
async
腳本不能保證加載順序
動(dòng)態(tài)創(chuàng)建
script
標(biāo)簽
動(dòng)態(tài)創(chuàng)建的 script
挨队,設(shè)置 src
并不會(huì)開(kāi)始下載谷暮,而是要添加到文檔中,JS文件才會(huì)開(kāi)始下載瞒瘸。
XHR 異步加載JS
9. ES5有幾種方式可以實(shí)現(xiàn)繼承坷备?分別有哪些優(yōu)缺點(diǎn)熄浓?
ES5 有 6 種方式可以實(shí)現(xiàn)繼承情臭,分別為:
1. 原型鏈繼承
原型鏈繼承的基本思想是利用原型讓一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法。
缺點(diǎn):
- 通過(guò)原型來(lái)實(shí)現(xiàn)繼承時(shí)赌蔑,原型會(huì)變成另一個(gè)類(lèi)型的實(shí)例俯在,原先的實(shí)例屬性變成了現(xiàn)在的原型屬性,該原型的引用類(lèi)型屬性會(huì)被所有的實(shí)例共享娃惯。
- 在創(chuàng)建子類(lèi)型的實(shí)例時(shí)跷乐,沒(méi)有辦法在不影響所有對(duì)象實(shí)例的情況下給超類(lèi)型的構(gòu)造函數(shù)中傳遞參數(shù)。
2. 借用構(gòu)造函數(shù)
借用構(gòu)造函數(shù)的技術(shù)趾浅,其基本思想為:
在子類(lèi)型的構(gòu)造函數(shù)中調(diào)用超類(lèi)型構(gòu)造函數(shù)愕提。
優(yōu)點(diǎn):
- 可以向超類(lèi)傳遞參數(shù)
- 解決了原型中包含引用類(lèi)型值被所有實(shí)例共享的問(wèn)題
缺點(diǎn):
- 方法都在構(gòu)造函數(shù)中定義,函數(shù)復(fù)用無(wú)從談起皿哨,另外超類(lèi)型原型中定義的方法對(duì)于子類(lèi)型而言都是不可見(jiàn)的浅侨。
3. 組合繼承(原型鏈 + 借用構(gòu)造函數(shù))
組合繼承指的是將原型鏈和借用構(gòu)造函數(shù)技術(shù)組合到一塊,從而發(fā)揮二者之長(zhǎng)的一種繼承模式证膨∪缡洌基本思路:
使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承央勒,既通過(guò)在原型上定義方法來(lái)實(shí)現(xiàn)了函數(shù)復(fù)用不见,又保證了每個(gè)實(shí)例都有自己的屬性。
缺點(diǎn):
- 無(wú)論什么情況下崔步,都會(huì)調(diào)用兩次超類(lèi)型構(gòu)造函數(shù):一次是在創(chuàng)建子類(lèi)型原型的時(shí)候稳吮,另一次是在子類(lèi)型構(gòu)造函數(shù)內(nèi)部。
優(yōu)點(diǎn):
- 可以向超類(lèi)傳遞參數(shù)
- 每個(gè)實(shí)例都有自己的屬性
- 實(shí)現(xiàn)了函數(shù)復(fù)用
4. 原型式繼承
原型繼承的基本思想:
借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象井濒,同時(shí)還不必因此創(chuàng)建自定義類(lèi)型盖高。
在 object()
函數(shù)內(nèi)部,先穿甲一個(gè)臨時(shí)性的構(gòu)造函數(shù)眼虱,然后將傳入的對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型喻奥,最后返回了這個(gè)臨時(shí)類(lèi)型的一個(gè)新實(shí)例,從本質(zhì)上講捏悬,object()
對(duì)傳入的對(duì)象執(zhí)行了一次淺拷貝撞蚕。
ECMAScript5通過(guò)新增 Object.create()
方法規(guī)范了原型式繼承。這個(gè)方法接收兩個(gè)參數(shù):一個(gè)用作新對(duì)象原型的對(duì)象和(可選的)一個(gè)為新對(duì)象定義額外屬性的對(duì)象(可以覆蓋原型對(duì)象上的同名屬性)过牙,在傳入一個(gè)參數(shù)的情況下甥厦,Object.create()
和 object()
方法的行為相同纺铭。
在沒(méi)有必要?jiǎng)?chuàng)建構(gòu)造函數(shù),僅讓一個(gè)對(duì)象與另一個(gè)對(duì)象保持相似的情況下刀疙,原型式繼承是可以勝任的舶赔。
缺點(diǎn):
同原型鏈實(shí)現(xiàn)繼承一樣,包含引用類(lèi)型值的屬性會(huì)被所有實(shí)例共享谦秧。
5. 寄生式繼承
寄生式繼承是與原型式繼承緊密相關(guān)的一種思路竟纳。寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類(lèi)似,即創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù)疚鲤,該函數(shù)在內(nèi)部已某種方式來(lái)增強(qiáng)對(duì)象锥累,最后再像真地是它做了所有工作一樣返回對(duì)象。
基于 person
返回了一個(gè)新對(duì)象 -—— person2
集歇,新對(duì)象不僅具有 person
的所有屬性和方法桶略,而且還有自己的 sayHi()
方法。在考慮對(duì)象而不是自定義類(lèi)型和構(gòu)造函數(shù)的情況下诲宇,寄生式繼承也是一種有用的模式际歼。
缺點(diǎn):
- 使用寄生式繼承來(lái)為對(duì)象添加函數(shù),會(huì)由于不能做到函數(shù)復(fù)用而效率低下姑蓝。
- 同原型鏈實(shí)現(xiàn)繼承一樣鹅心,包含引用類(lèi)型值的屬性會(huì)被所有實(shí)例共享。
6. 寄生組合式繼承
所謂寄生組合式繼承它掂,即通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性巴帮,通過(guò)原型鏈的混成形式來(lái)繼承方法,基本思路:
不必為了指定子類(lèi)型的原型而調(diào)用超類(lèi)型的構(gòu)造函數(shù)虐秋,我們需要的僅是超類(lèi)型原型的一個(gè)副本榕茧,本質(zhì)上就是使用寄生式繼承來(lái)繼承超類(lèi)型的原型,然后再將結(jié)果指定給子類(lèi)型的原型客给。寄生組合式繼承的基本模式如下所示:
- 第一步:創(chuàng)建超類(lèi)型原型的一個(gè)副本
- 第二步:為創(chuàng)建的副本添加
constructor
屬性 - 第三步:將新創(chuàng)建的對(duì)象賦值給子類(lèi)型的原型
至此用押,我們就可以通過(guò)調(diào)用 inheritPrototype
來(lái)替換為子類(lèi)型原型賦值的語(yǔ)句:
優(yōu)點(diǎn):
只調(diào)用了一次超類(lèi)構(gòu)造函數(shù),效率更高靶剑。避免在SuberType.prototype
上面創(chuàng)建不必要的蜻拨、多余的屬性鸽照,與其同時(shí)溅话,原型鏈還能保持不變。
因此寄生組合繼承是引用類(lèi)型最理性的繼承范式蜗顽。
10. 隱藏頁(yè)面中的某個(gè)元素的方法有哪些坑匠?
隱藏類(lèi)型
屏幕并不是唯一的輸出機(jī)制血崭,比如說(shuō)屏幕上看不見(jiàn)的元素(隱藏的元素),其中一些依然能夠被讀屏軟件閱讀出來(lái)(因?yàn)樽x屏軟件依賴(lài)于可訪問(wèn)性樹(shù)來(lái)闡述)。為了消除它們之間的歧義夹纫,我們將其歸為三大類(lèi):
完全隱藏:元素從渲染樹(shù)中消失咽瓷,不占據(jù)空間。
視覺(jué)上的隱藏:屏幕中不可見(jiàn)舰讹,占據(jù)空間茅姜。
語(yǔ)義上的隱藏:讀屏軟件不可讀,但正常占據(jù)空月匣。
完全隱藏
1.display 屬性
display: none;
復(fù)制代碼2.hidden 屬性
HTML5 新增屬性钻洒,相當(dāng)于 display: none
<div hidden>
</div>
復(fù)制代碼
視覺(jué)上的隱藏
1.利用 position 和 盒模型 將元素移出可視區(qū)范圍
設(shè)置 posoition 為 absolute 或 fixed,通過(guò)設(shè)置 top桶错、left 等值航唆,將其移出可視區(qū)域胀蛮。
position:absolute;
left: -99999px;
復(fù)制代碼
設(shè)置 position 為 relative院刁,通過(guò)設(shè)置 top、left 等值粪狼,將其移出可視區(qū)域退腥。
position: relative;
left: -99999px;
height: 0
復(fù)制代碼
設(shè)置 margin 值,將其移出可視區(qū)域范圍(可視區(qū)域占位)再榄。
margin-left: -99999px;
height: 0;
復(fù)制代碼2.利用 transfrom
縮放
transform: scale(0);
height: 0;
復(fù)制代碼
移動(dòng) translateX
,translateY
transform: translateX(-99999px);
height: 0
復(fù)制代碼
旋轉(zhuǎn) rotate
transform: rotateY(90deg);
復(fù)制代碼3.設(shè)置其大小為0
寬高為0狡刘,字體大小為0:
height: 0;
width: 0;
font-size: 0;
復(fù)制代碼
寬高為0,超出隱藏:
height: 0;
width: 0;
overflow: hidden;
復(fù)制代碼4.設(shè)置透明度為0
opacity: 0;
復(fù)制代碼5.visibility屬性
visibility: hidden;
復(fù)制代碼6.層級(jí)覆蓋困鸥,z-index 屬性
position: relative;
z-index: -999;
復(fù)制代碼再設(shè)置一個(gè)層級(jí)較高的元素覆蓋在此元素上嗅蔬。
7.clip-path 裁剪
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
復(fù)制代碼
語(yǔ)義上的隱藏
aria-hidden 屬性
讀屏軟件不可讀,占據(jù)空間疾就,可見(jiàn)澜术。
<div aria-hidden="true">
</div>
11. let、const猬腰、var 的區(qū)別有哪些鸟废?
聲明方式 | 變量提升 | 暫時(shí)性死區(qū) | 重復(fù)聲明 | 塊作用域有效 | 初始值 | 重新賦值 |
---|---|---|---|---|---|---|
var | 會(huì) | 不存在 | 允許 | 不是 | 非必須 | 允許 |
let | 不會(huì) | 存在 | 不允許 | 是 | 非必須 | 允許 |
const | 不會(huì) | 存在 | 不允許 | 是 | 必須 | 不允許 |
1.let/const 定義的變量不會(huì)出現(xiàn)變量提升,而 var 定義的變量會(huì)提升姑荷。
2.相同作用域中盒延,let 和 const 不允許重復(fù)聲明,var 允許重復(fù)聲明鼠冕。
3.const 聲明變量時(shí)必須設(shè)置初始值
4.const 聲明一個(gè)只讀的常量添寺,這個(gè)常量不可改變。
這里有一個(gè)非常重要的點(diǎn)即是:在JS中懈费,復(fù)雜數(shù)據(jù)類(lèi)型计露,存儲(chǔ)在棧中的是堆內(nèi)存的地址,存在棧中的這個(gè)地址是不變的,但是存在堆中的值是可以變得薄坏。有沒(méi)有相當(dāng)常量指針/指針常量~
一圖勝萬(wàn)言趋厉,如下圖所示,不變的是棧內(nèi)存中 a 存儲(chǔ)的 20胶坠,和 b 中存儲(chǔ)的 0x0012ff21(瞎編的一個(gè)數(shù)字)君账。而 {age: 18, star: 200} 是可變的。
12. 說(shuō)一說(shuō)你對(duì)JS執(zhí)行上下文棧和作用域鏈的理解沈善?
在開(kāi)始說(shuō)明JS上下文棧和作用域之前乡数,我們先說(shuō)明下JS上下文以及作用域的概念。
JS執(zhí)行上下文
執(zhí)行上下文就是當(dāng)前 JavaScript 代碼被解析和執(zhí)行時(shí)所在環(huán)境的抽象概念闻牡, JavaScript 中運(yùn)行任何的代碼都是在執(zhí)行上下文中運(yùn)行净赴。
執(zhí)行上下文類(lèi)型分為:
- 全局執(zhí)行上下文
- 函數(shù)執(zhí)行上下文
執(zhí)行上下文創(chuàng)建過(guò)程中,需要做以下幾件事:
- 創(chuàng)建變量對(duì)象:首先初始化函數(shù)的參數(shù)arguments罩润,提升函數(shù)聲明和變量聲明玖翅。
- 創(chuàng)建作用域鏈(Scope Chain):在執(zhí)行期上下文的創(chuàng)建階段,作用域鏈?zhǔn)窃谧兞繉?duì)象之后創(chuàng)建的割以。
- 確定this的值金度,即 ResolveThisBinding
作用域
作用域負(fù)責(zé)收集和維護(hù)由所有聲明的標(biāo)識(shí)符(變量)組成的一系列查詢(xún),并實(shí)施一套非常嚴(yán)格的規(guī)則严沥,確定當(dāng)前執(zhí)行的代碼對(duì)這些標(biāo)識(shí)符的訪問(wèn)權(quán)限猜极。—— 摘錄自《你不知道的JavaScript》(上卷)
作用域有兩種工作模型:詞法作用域和動(dòng)態(tài)作用域消玄,JS采用的是詞法作用域工作模型跟伏,詞法作用域意味著作用域是由書(shū)寫(xiě)代碼時(shí)變量和函數(shù)聲明的位置決定的。(with
和 eval
能夠修改詞法作用域翩瓜,但是不推薦使用受扳,對(duì)此不做特別說(shuō)明)
作用域分為:
- 全局作用域
- 函數(shù)作用域
- 塊級(jí)作用域
JS執(zhí)行上下文棧(后面簡(jiǎn)稱(chēng)執(zhí)行棧)
執(zhí)行棧,也叫做調(diào)用棧奥溺,具有 LIFO (后進(jìn)先出) 結(jié)構(gòu)辞色,用于存儲(chǔ)在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。
規(guī)則如下:
- 首次運(yùn)行JavaScript代碼的時(shí)候,會(huì)創(chuàng)建一個(gè)全局執(zhí)行的上下文并Push到當(dāng)前的執(zhí)行棧中浮定,每當(dāng)發(fā)生函數(shù)調(diào)用相满,引擎都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的函數(shù)執(zhí)行上下文并Push當(dāng)前執(zhí)行棧的棧頂。
- 當(dāng)棧頂?shù)暮瘮?shù)運(yùn)行完成后桦卒,其對(duì)應(yīng)的函數(shù)執(zhí)行上下文將會(huì)從執(zhí)行棧中Pop出立美,上下文的控制權(quán)將移動(dòng)到當(dāng)前執(zhí)行棧的下一個(gè)執(zhí)行上下文。
以一段代碼具體說(shuō)明:
Global Execution Context
(即全局執(zhí)行上下文)首先入棧方灾,過(guò)程如下:
偽代碼:
//全局執(zhí)行上下文首先入棧
ECStack.push(globalContext);
//執(zhí)行fun1();
ECStack.push(<fun1> functionContext);
//fun1中又調(diào)用了fun2;
ECStack.push(<fun2> functionContext);
//fun2中又調(diào)用了fun3;
ECStack.push(<fun3> functionContext);
//fun3執(zhí)行完畢
ECStack.pop();
//fun2執(zhí)行完畢
ECStack.pop();
//fun1執(zhí)行完畢
ECStack.pop();
//javascript繼續(xù)順序執(zhí)行下面的代碼建蹄,但ECStack底部始終有一個(gè) 全局上下文(globalContext);
復(fù)制代碼
作用域鏈
作用域鏈就是從當(dāng)前作用域開(kāi)始一層一層向上尋找某個(gè)變量碌更,直到找到全局作用域還是沒(méi)找到,就宣布放棄洞慎。這種一層一層的關(guān)系痛单,就是作用域鏈。
如:
fn2作用域鏈 = [fn2作用域, fn1作用域劲腿,全局作用域]
13. 防抖函數(shù)的作用是什么旭绒?請(qǐng)實(shí)現(xiàn)一個(gè)防抖函數(shù)
防抖函數(shù)的作用
防抖函數(shù)的作用就是控制函數(shù)在一定時(shí)間內(nèi)的執(zhí)行次數(shù)。防抖意味著N秒內(nèi)函數(shù)只會(huì)被執(zhí)行一次焦人,如果N秒內(nèi)再次被觸發(fā)挥吵,則重新計(jì)算延遲時(shí)間。
舉例說(shuō)明: 小思最近在減肥花椭,但是她非常吃吃零食忽匈。為此,與其男朋友約定好矿辽,如果10天不吃零食丹允,就可以購(gòu)買(mǎi)一個(gè)包(不要問(wèn)為什么是包,因?yàn)?strong>包治百病)嗦锐。但是如果中間吃了一次零食嫌松,那么就要重新計(jì)算時(shí)間沪曙,直到小思堅(jiān)持10天沒(méi)有吃零食奕污,才能購(gòu)買(mǎi)一個(gè)包。所以液走,管不住嘴的小思碳默,沒(méi)有機(jī)會(huì)買(mǎi)包(悲傷的故事)... 這就是 防抖。
防抖函數(shù)實(shí)現(xiàn)
- 事件第一次觸發(fā)時(shí)缘眶,
timer
是null
嘱根,調(diào)用later()
,若immediate
為true
巷懈,那么立即調(diào)用func.apply(this, params)
该抒;如果immediate
為false
,那么過(guò)wait
之后顶燕,調(diào)用func.apply(this, params)
- 事件第二次觸發(fā)時(shí)凑保,如果
timer
已經(jīng)重置為null
(即setTimeout
的倒計(jì)時(shí)結(jié)束),那么流程與第一次觸發(fā)時(shí)一樣涌攻,若timer
不為null
(即 setTimeout 的倒計(jì)時(shí)未結(jié)束)欧引,那么清空定時(shí)器,重新開(kāi)始計(jì)時(shí)恳谎。
immediate
為 true 時(shí)芝此,表示函數(shù)在每個(gè)等待時(shí)延的開(kāi)始被調(diào)用憋肖。immediate
為 false 時(shí),表示函數(shù)在每個(gè)等待時(shí)延的結(jié)束被調(diào)用婚苹。
防抖的應(yīng)用場(chǎng)景
- 搜索框輸入查詢(xún)岸更,如果用戶(hù)一直在輸入中,沒(méi)有必要不停地調(diào)用去請(qǐng)求服務(wù)端接口膊升,等用戶(hù)停止輸入的時(shí)候坐慰,再調(diào)用,設(shè)置一個(gè)合適的時(shí)間間隔用僧,有效減輕服務(wù)端壓力结胀。
- 表單驗(yàn)證
- 按鈕提交事件。
- 瀏覽器窗口縮放责循,resize事件(如窗口停止改變大小之后重新計(jì)算布局)等糟港。
14. 節(jié)流函數(shù)的作用是什么?有哪些應(yīng)用場(chǎng)景院仿,請(qǐng)實(shí)現(xiàn)一個(gè)節(jié)流函數(shù)
節(jié)流函數(shù)的作用
節(jié)流函數(shù)的作用是規(guī)定一個(gè)單位時(shí)間秸抚,在這個(gè)單位時(shí)間內(nèi)最多只能觸發(fā)一次函數(shù)執(zhí)行,如果這個(gè)單位時(shí)間內(nèi)多次觸發(fā)函數(shù)歹垫,只能有一次生效剥汤。
節(jié)流函數(shù)實(shí)現(xiàn)
禁用第一次首先執(zhí)行,傳遞 {leading: false}
排惨;想禁用最后一次執(zhí)行吭敢,傳遞 {trailing: false}
節(jié)流的應(yīng)用場(chǎng)景
- 按鈕點(diǎn)擊事件
- 拖拽事件
- onScoll
- 計(jì)算鼠標(biāo)移動(dòng)的距離(mousemove)
15. 什么是閉包?閉包的作用是什么暮芭?
閉包的定義
《JavaScript高級(jí)程序設(shè)計(jì)》:
閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)
《JavaScript權(quán)威指南》:
從技術(shù)的角度講鹿驼,所有的JavaScript函數(shù)都是閉包:它們都是對(duì)象,它們都關(guān)聯(lián)到作用域鏈辕宏。
《你不知道的JavaScript》
當(dāng)函數(shù)可以記住并訪問(wèn)所在的詞法作用域時(shí)畜晰,就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行瑞筐。
創(chuàng)建一個(gè)閉包
閉包使得函數(shù)可以繼續(xù)訪問(wèn)定義時(shí)的詞法作用域凄鼻。拜 fn 所賜,在 foo() 執(zhí)行后聚假,foo 內(nèi)部作用域不會(huì)被銷(xiāo)毀块蚌。
閉包的作用
能夠訪問(wèn)函數(shù)定義時(shí)所在的詞法作用域(阻止其被回收)。
私有化變量
- 模擬塊級(jí)作用域
- 創(chuàng)建模塊
模塊模式具有兩個(gè)必備的條件(來(lái)自《你不知道的JavaScript》)
- 必須有外部的封閉函數(shù)魔策,該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)
- 封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù)匈子,這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問(wèn)或者修改私有的狀態(tài)闯袒。
16. 實(shí)現(xiàn) Promise.all 方法
在實(shí)現(xiàn) Promise.all 方法之前虎敦,我們首先要知道 Promise.all 的功能和特點(diǎn)游岳,因?yàn)樵谇宄?Promise.all 功能和特點(diǎn)的情況下,我們才能進(jìn)一步去寫(xiě)實(shí)現(xiàn)其徙。
Promise.all 功能
Promise.all(iterable)
返回一個(gè)新的 Promise 實(shí)例胚迫。此實(shí)例在 iterable
參數(shù)內(nèi)所有的 promise
都 fulfilled
或者參數(shù)中不包含 promise
時(shí),狀態(tài)變成 fulfilled
唾那;如果參數(shù)中 promise
有一個(gè)失敗rejected
访锻,此實(shí)例回調(diào)失敗,失敗原因的是第一個(gè)失敗 promise
的返回結(jié)果闹获。
let p = Promise.all([p1, p2, p3]);
復(fù)制代碼
p的狀態(tài)由 p1,p2,p3決定期犬,分成以下;兩種情況:
(1)只有p1避诽、p2龟虎、p3的狀態(tài)都變成 fulfilled
,p的狀態(tài)才會(huì)變成 fulfilled
沙庐,此時(shí)p1鲤妥、p2、p3的返回值組成一個(gè)數(shù)組拱雏,傳遞給p的回調(diào)函數(shù)棉安。
(2)只要p1、p2铸抑、p3之中有一個(gè)被 rejected
贡耽,p的狀態(tài)就變成 rejected
,此時(shí)第一個(gè)被reject的實(shí)例的返回值羡滑,會(huì)傳遞給p的回調(diào)函數(shù)菇爪。
Promise.all 的特點(diǎn)
Promise.all 的返回值是一個(gè) promise 實(shí)例
- 如果傳入的參數(shù)為空的可迭代對(duì)象,
Promise.all
會(huì) 同步 返回一個(gè)已完成狀態(tài)的promise
- 如果傳入的參數(shù)中不包含任何 promise,
Promise.all
會(huì) 異步 返回一個(gè)已完成狀態(tài)的promise
- 其它情況下柒昏,
Promise.all
返回一個(gè) 處理中(pending) 狀態(tài)的promise
.
Promise.all 返回的 promise 的狀態(tài)
- 如果傳入的參數(shù)中的 promise 都變成完成狀態(tài),
Promise.all
返回的promise
異步地變?yōu)橥瓿伞?/li> - 如果傳入的參數(shù)中熙揍,有一個(gè)
promise
失敗职祷,Promise.all
異步地將失敗的那個(gè)結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其它promise
是否完成 - 在任何情況下届囚,
Promise.all
返回的promise
的完成狀態(tài)的結(jié)果都是一個(gè)數(shù)組
Promise.all 實(shí)現(xiàn)
17. 請(qǐng)實(shí)現(xiàn)一個(gè) flattenDeep 函數(shù)有梆,把嵌套的數(shù)組扁平化
例如:
flattenDeep([1, [2, [3, [4]], 5]]); //[1, 2, 3, 4, 5]
復(fù)制代碼
利用 Array.prototype.flat
ES6 為數(shù)組實(shí)例新增了 flat
方法,用于將嵌套的數(shù)組“拉平”意系,變成一維的數(shù)組。該方法返回一個(gè)新數(shù)組,對(duì)原數(shù)組沒(méi)有影響宅静。
flat
默認(rèn)只會(huì) “拉平” 一層,如果想要 “拉平” 多層的嵌套數(shù)組兜辞,需要給 flat
傳遞一個(gè)整數(shù),表示想要拉平的層數(shù)夸溶。
當(dāng)傳遞的整數(shù)大于數(shù)組嵌套的層數(shù)時(shí)逸吵,會(huì)將數(shù)組拉平為一維數(shù)組,JS能表示的最大數(shù)字為 Math.pow(2, 53) - 1
缝裁,因此我們可以這樣定義 flattenDeep
函數(shù)
利用 reduce 和 concat
使用 stack 無(wú)限反嵌套多層嵌套數(shù)組
18. 請(qǐng)實(shí)現(xiàn)一個(gè) uniq 函數(shù)扫皱,實(shí)現(xiàn)數(shù)組去重
例如:
uniq([1, 2, 3, 5, 3, 2]);//[1, 2, 3, 5復(fù)制代碼
法1: 利用ES6新增數(shù)據(jù)類(lèi)型
Set
Set
類(lèi)似于數(shù)組,但是成員的值都是唯一的捷绑,沒(méi)有重復(fù)的值韩脑。
法2: 利用
indexOf
法3: 利用
includes
法4:利用
reduce
法5:利用
Map
19. 可迭代對(duì)象有哪些特點(diǎn)
ES6 規(guī)定,默認(rèn)的 Iterator
接口部署在數(shù)據(jù)結(jié)構(gòu)的 Symbol.iterator
屬性粹污,換個(gè)角度扰才,也可以認(rèn)為,一個(gè)數(shù)據(jù)結(jié)構(gòu)只要具有 Symbol.iterator
屬性(Symbol.iterator
方法對(duì)應(yīng)的是遍歷器生成函數(shù)厕怜,返回的是一個(gè)遍歷器對(duì)象)衩匣,那么就可以其認(rèn)為是可迭代的。
可迭代對(duì)象的特點(diǎn)
- 具有
Symbol.iterator
屬性粥航,Symbol.iterator()
返回的是一個(gè)遍歷器對(duì)象 - 可以使用
for ... of
進(jìn)行循環(huán) - 通過(guò)被
Array.from
轉(zhuǎn)換為數(shù)組
原生具有 Iterator
接口的數(shù)據(jù)結(jié)構(gòu):
- Array
- Map
- Set
- String
- TypedArray
- 函數(shù)的 arguments 對(duì)象
- NodeList 對(duì)象
20. JSONP 的原理是什么琅捏?
盡管瀏覽器有同源策略,但是 <script>
標(biāo)簽的 src
屬性不會(huì)被同源策略所約束递雀,可以獲取任意服務(wù)器上的腳本并執(zhí)行柄延。jsonp
通過(guò)插入 script
標(biāo)簽的方式來(lái)實(shí)現(xiàn)跨域,參數(shù)只能通過(guò) url
傳入缀程,僅能支持 get
請(qǐng)求搜吧。
實(shí)現(xiàn)原理:
- Step1: 創(chuàng)建 callback 方法
- Step2: 插入 script 標(biāo)簽
- Step3: 后臺(tái)接受到請(qǐng)求,解析前端傳過(guò)去的 callback 方法杨凑,返回該方法的調(diào)用滤奈,并且數(shù)據(jù)作為參數(shù)傳入該方法
- Step4: 前端執(zhí)行服務(wù)端返回的方法調(diào)用
jsonp源碼實(shí)現(xiàn)
使用:
服務(wù)端代碼(node):
參考文章:
[1] 珠峰架構(gòu)課(墻裂推薦)
[2] [JavaScript高級(jí)程序設(shè)計(jì)第六章]
[3] Step-By-Step】高頻面試題深入解析 / 周刊01
[4] Step-By-Step】高頻面試題深入解析 / 周刊02
[5] Step-By-Step】高頻面試題深入解析 / 周刊03
[6] Step-By-Step】高頻面試題深入解析 / 周刊04
作者:劉小夕
鏈接:https://juejin.im/post/5d124a12f265da1b9163a28d
來(lái)源:掘金
求點(diǎn)贊,求關(guān)注~