最近看到有一篇文章總結(jié)了一些前端的面試題,面向的對(duì)象應(yīng)該是社招中初、中級(jí)的前端,感覺(jué)有一定的參考價(jià)值,因此開一個(gè)帖子嘗試解答這些問(wèn)題,順便當(dāng)做自己的面試題積累。愛(ài)掏網(wǎng) - it200.com
原文鏈接戳這里
JavaScript基礎(chǔ)
在網(wǎng)上找到一篇文章,里面有一道面試題,考察了包括變量定義提升、this指針指向、運(yùn)算符優(yōu)先級(jí)、原型、繼承、全局變量污染、對(duì)象屬性及原型屬性優(yōu)先級(jí)等許多知識(shí)點(diǎn),而就其中聲明提前相關(guān)的知識(shí),我覺(jué)得也十分有參考價(jià)值:
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
// 請(qǐng)寫出以下輸出結(jié)果:
Foo.getName();
getName(); // 聲明提前
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
復(fù)制代碼
這道題的答案是:2、4、1、1、2、3、3。愛(ài)掏網(wǎng) - it200.com
這里考察聲明提前的題目在代碼中已經(jīng)標(biāo)出,這里聲明getName方法的兩個(gè)語(yǔ)句:
var getName = function () { alert (4) };
function getName() { alert (5) }
復(fù)制代碼
實(shí)際上在解析的時(shí)候是這樣的順序:
function getName() { alert (5) }
var getName;
getName = function () { alert (4) };
復(fù)制代碼
如果我們?cè)诖a中間再加兩個(gè)斷點(diǎn):
getName(); // 5 var getName = function () { alert (4) };
getName(); // 4 function getName() { alert (5) }
復(fù)制代碼
在第一次getName時(shí),function的聲明和var的聲明都被提前到了第一次getName的前面,而getName的賦值操作并不會(huì)提前,單純使用var的聲明也不會(huì)覆蓋function所定義的變量,因此第一次getName輸出的是function聲明的5; 而第二次getName則是發(fā)生在賦值語(yǔ)句的后面,因此輸出的結(jié)果是4,所以實(shí)際代碼的執(zhí)行順序是這樣:
function getName() { alert (5) }
var getName;
getName(); // 5
getName = function () { alert (4) };
getName(); // 4 復(fù)制代碼
localStorage,sessionStorage和cookie的區(qū)別
共同點(diǎn):都是保存在瀏覽器端、僅同源可用的存儲(chǔ)方式
- 數(shù)據(jù)存儲(chǔ)方面
- cookie數(shù)據(jù)始終在同源的http請(qǐng)求中攜帶(即使不需要),即cookie在瀏覽器和服務(wù)器間來(lái)回傳遞。愛(ài)掏網(wǎng) - it200.comcookie數(shù)據(jù)還有路徑(path)的概念,可以限制cookie只屬于某個(gè)路徑下
- sessionStorage和localStorage不會(huì)自動(dòng)把數(shù)據(jù)發(fā)送給服務(wù)器,僅在本地保存。愛(ài)掏網(wǎng) - it200.com
- 存儲(chǔ)數(shù)據(jù)大小
- 存儲(chǔ)大小限制也不同,cookie數(shù)據(jù)不能超過(guò)4K,同時(shí)因?yàn)槊看蝖ttp請(qǐng)求都會(huì)攜帶cookie、所以cookie只適合保存很小的數(shù)據(jù),如會(huì)話標(biāo)識(shí)。愛(ài)掏網(wǎng) - it200.com
- sessionStorage和localStorage雖然也有存儲(chǔ)大小的限制,但比cookie大得多,可以達(dá)到5M或更大
- 數(shù)據(jù)存儲(chǔ)有效期
- sessionStorage:僅在當(dāng)前瀏覽器窗口關(guān)閉之前有效;
- localStorage:始終有效,窗口或?yàn)g覽器關(guān)閉也一直保存,本地存儲(chǔ),因此用作持久數(shù)據(jù);
- cookie:只在設(shè)置的cookie過(guò)期時(shí)間之前有效,即使窗口關(guān)閉或?yàn)g覽器關(guān)閉
- 作用域不同
- sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個(gè)頁(yè)面;
- localstorage在所有同源窗口中都是共享的;也就是說(shuō)只要瀏覽器不關(guān)閉,數(shù)據(jù)仍然存在
- cookie: 也是在所有同源窗口中都是共享的.也就是說(shuō)只要瀏覽器不關(guān)閉,數(shù)據(jù)仍然存在
不久我寫了一個(gè)帖子,對(duì)同源策略及各種跨域的方式進(jìn)行了總結(jié):什么是跨域,為什么瀏覽器會(huì)禁止跨域,及其引起的發(fā)散性學(xué)習(xí)
Promise是ES6加入的新特性,用于更合理的解決異步編程問(wèn)題,關(guān)于用法阮一峰老師在ECMAScript 6 入門中作出了詳細(xì)的說(shuō)明,在此就不重復(fù)了。愛(ài)掏網(wǎng) - it200.com
30分鐘,讓你徹底明白Promise原理
上面這篇文章則是對(duì)Promise的原理進(jìn)行的詳細(xì)的說(shuō)明,在這里,我提取最簡(jiǎn)單的Promise實(shí)現(xiàn)方式來(lái)對(duì)Promise的原理進(jìn)行說(shuō)明:
function Promise(fn) {
var value = null,
callbacks = []; // callbacks為數(shù)組,因?yàn)榭赡芡瑫r(shí)有很多個(gè)回調(diào) this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
};
function resolve(value) {
callbacks.forEach(function (callback) {
callback(value);
});
}
fn(resolve);
}
復(fù)制代碼
首先,then
里面聲明的單個(gè)或多個(gè)函數(shù),將被推入callbacks
列表,在Promise實(shí)例調(diào)用resolve
方法時(shí)遍歷調(diào)用,并傳入resolve
方法中傳入的參數(shù)值。愛(ài)掏網(wǎng) - it200.com
以下,使用一個(gè)簡(jiǎn)單的例子來(lái)對(duì)Promise的執(zhí)行流程進(jìn)行分析:
functionm func () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve('complete')
}, 3000);
})
}
func().then(function (res) {
console.log(res); // complete
})
復(fù)制代碼
func
函數(shù)的定義是返回了一個(gè)Promise實(shí)例,聲明實(shí)例時(shí)傳入的回調(diào)函數(shù)加入了一個(gè)resolve
參數(shù)(這個(gè)resolve
參數(shù)在Promise中的fn(resolve)
定義中獲取resolve
的函數(shù)實(shí)體),回調(diào)中執(zhí)行了一個(gè)異步操作,在異步操作完成的回調(diào)中執(zhí)行了resolve
函數(shù)。愛(ài)掏網(wǎng) - it200.com
再看執(zhí)行步驟,func
函數(shù)返回了一個(gè)Promise實(shí)例,實(shí)例則可以執(zhí)行Promise構(gòu)造函數(shù)中定義的then
方法,then
方法中傳入的回調(diào)則會(huì)在resolve
(即異步操作完成后)執(zhí)行,由此實(shí)現(xiàn)了通過(guò)then
方法執(zhí)行異步操作完成后回調(diào)的功能。愛(ài)掏網(wǎng) - it200.com
原文中貼出的文章具有很大參考價(jià)值,先貼個(gè)鏈接:詳解JavaScript中的Event Loop(事件循環(huán))機(jī)制。愛(ài)掏網(wǎng) - it200.com
JavaScript是一種單線程、非阻塞的語(yǔ)言,這是由于它當(dāng)初的設(shè)計(jì)就是用于和瀏覽器交互的:
- 單線程:
JavaScript
設(shè)計(jì)為單線程的原因是,最開始它最大的作用就是和DOM
進(jìn)行交互,試想一下,如果JavaScript
是多線程的,那么當(dāng)兩個(gè)線程同時(shí)對(duì)DOM
進(jìn)行一項(xiàng)操作,例如一個(gè)向其添加事件,而另一個(gè)刪除了這個(gè)DOM
,此時(shí)該如何處理呢?因此,為了保證不會(huì) 發(fā)生類似于這個(gè)例子中的情景,JavaScript
選擇只用一個(gè)主線程來(lái)執(zhí)行代碼,這樣就保證了程序執(zhí)行的一致性。愛(ài)掏網(wǎng) - it200.com - 非阻塞:當(dāng)代碼需要進(jìn)行一項(xiàng)異步任務(wù)(無(wú)法立刻返回結(jié)果,需要花一定時(shí)間才能返回的任務(wù),如I/O事件)的時(shí)候,主線程會(huì)掛起(
pending
)這個(gè)任務(wù),然后在異步任務(wù)返回結(jié)果的時(shí)候再根據(jù)一定規(guī)則去執(zhí)行相應(yīng)的回調(diào)。愛(ài)掏網(wǎng) - it200.com而JavaScript
實(shí)現(xiàn)異步操作的方法就是使用Event Loop。愛(ài)掏網(wǎng) - it200.com
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
復(fù)制代碼
下面通過(guò)一段代碼來(lái)分析這個(gè)問(wèn)題,首先setTimeout
和Promise
中的then
回調(diào)都是異步方法,而new Promise
則是一個(gè)同步操作,所以這段代碼應(yīng)該首先會(huì)立即輸出2
;JavaScript
將異步方法分為了marco task
(宏任務(wù):包括setTimeout
和setInterval
等)和micro task
(微任務(wù):包括new Promise
等),在JavaScript
的執(zhí)行棧中,如果同時(shí)存在到期的宏任務(wù)和微任務(wù),則會(huì)將微任務(wù)先全部執(zhí)行,再執(zhí)行第一個(gè)宏任務(wù),因此,兩個(gè)異步操作中then
的回調(diào)會(huì)率先執(zhí)行,然后才執(zhí)行setTimeout
的回調(diào),因此會(huì)依次輸出3、1,所以最終輸出的結(jié)果就是2、3、1。愛(ài)掏網(wǎng) - it200.com
這個(gè)問(wèn)題阮一峰老師在ECMAScript 6 入門中的let 和 const 命令
章節(jié)對(duì)這個(gè)問(wèn)題作出了詳細(xì)的說(shuō)明,下面提取一些我認(rèn)為關(guān)鍵的點(diǎn)進(jìn)行講解。愛(ài)掏網(wǎng) - it200.com
ES6引入了使用{}
包裹的代碼區(qū)域作為塊級(jí)作用域的聲明方式,其效果與ES5中function
聲明的函數(shù)所生成的函數(shù)作用域具有相同的效果,作用域外部不能訪問(wèn)作用域內(nèi)部聲明的函數(shù)或變量,這樣的聲明在ES6中對(duì)于包括for () {}
、if () {}
等大括號(hào)包裹的代碼塊中都會(huì)生效,生成一個(gè)單獨(dú)的作用域。愛(ài)掏網(wǎng) - it200.com
ES6新增的let
聲明變量的方式相比var
具有以下幾個(gè)重要特點(diǎn):
-
let
聲明的變量只在作用域內(nèi)有效,如下方代碼,if
聲明生成了一個(gè)塊級(jí)作用域,在這個(gè)作用域內(nèi)聲明的變量在作用域外部無(wú)法訪問(wèn),假如訪問(wèn)會(huì)產(chǎn)生錯(cuò)誤:
if (true) {
let me = 'handsome boy';
}
console.log(me); // ReferenceError 復(fù)制代碼
-
let
聲明的變量與var
不同,不會(huì)產(chǎn)生變量提升,如下方代碼,在聲明之前輸出代碼,會(huì)產(chǎn)生錯(cuò)誤:
// var 的情況 console.log(foo); // 輸出undefined var foo = 2;
// let 的情況 console.log(bar); // ReferenceError let bar = 2;
復(fù)制代碼
-
let
的聲明方式不允許重復(fù)聲明,如重復(fù)聲明會(huì)報(bào)錯(cuò),而var
聲明變量時(shí),后聲明的語(yǔ)句會(huì)對(duì)先聲明的語(yǔ)句進(jìn)行覆蓋:
// 報(bào)錯(cuò) function func() {
let a = 10;
var a = 1;
}
// 報(bào)錯(cuò) function func() {
let a = 10;
let a = 1;
}
復(fù)制代碼
- 只要塊級(jí)作用域內(nèi)存在
let
命令,它所聲明的變量就“綁定”(binding
)這個(gè)區(qū)域,不再受外部的影響,這個(gè)特性稱為暫時(shí)性死區(qū)
。愛(ài)掏網(wǎng) - it200.com
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError let tmp;
}
復(fù)制代碼
待補(bǔ)充
待補(bǔ)充
參考:juejin.im/post/5a9923…
瀏覽器在接收到 html
與 css
后,渲染的步驟是:html
經(jīng)過(guò)渲染生成 DOM
樹, css
經(jīng)過(guò)渲染生成 css
渲染樹,兩者再經(jīng)過(guò)結(jié)合,生成 render tree
,瀏覽器就可以根據(jù) render tree
進(jìn)行畫面繪制。愛(ài)掏網(wǎng) - it200.com
如果瀏覽器從服務(wù)器接收到了新的 css
,需要更新頁(yè)面時(shí),需要經(jīng)過(guò)什么操作呢?這就是回流 reflow
與重繪 repaint
。愛(ài)掏網(wǎng) - it200.com由于瀏覽器在重新渲染頁(yè)面時(shí)會(huì)先進(jìn)行 reflow
再進(jìn)行 repaint
,因此,回流必將引起重繪,而重繪不一定會(huì)引起回流。愛(ài)掏網(wǎng) - it200.com
重繪:當(dāng)前元素的樣式(背景顏色、字體顏色等)發(fā)生改變的時(shí)候,我們只需要把改變的元素重新的渲染一下即可,重繪對(duì)瀏覽器的性能影響較小。愛(ài)掏網(wǎng) - it200.com發(fā)生重繪的情形:改變?nèi)萜鞯耐庥^風(fēng)格等,比如 background:black
等。愛(ài)掏網(wǎng) - it200.com改變外觀,不改變布局,不影響其他的 DOM
。愛(ài)掏網(wǎng) - it200.com
??
回流:是指瀏覽器為了重新渲染部分或者全部的文檔而重新計(jì)算文檔中元素的位置和幾何構(gòu)造的過(guò)程。愛(ài)掏網(wǎng) - it200.com
因?yàn)榛亓骺赡軐?dǎo)致整個(gè) DOM
樹的重新構(gòu)造,所以是性能的一大殺手,一個(gè)元素的回流導(dǎo)致了其所有子元素以及 DOM
中緊隨其后的祖先元素的隨后的回流。愛(ài)掏網(wǎng) - it200.com下面貼出會(huì)觸發(fā)瀏覽器 reflow
的變化:
- 頁(yè)面首次渲染
- 瀏覽器窗口大小發(fā)生改變
- 元素尺寸或位置發(fā)生改變
- 元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)
- 元素字體大小變化
- 添加或者刪除可見的DOM元素
- 激活CSS偽類(例如::hover)
- 查詢某些屬性或調(diào)用某些方法
優(yōu)化方案:
CSS
- 避免使用
table
布局。愛(ài)掏網(wǎng) - it200.com - 盡可能在
DOM
樹的最末端改變class
。愛(ài)掏網(wǎng) - it200.com - 避免設(shè)置多層內(nèi)聯(lián)樣式。愛(ài)掏網(wǎng) - it200.com
- 將動(dòng)畫效果應(yīng)用到
position
屬性為absolute
或fixed
的元素上。愛(ài)掏網(wǎng) - it200.com - 避免使用
CSS
表達(dá)式(例如:calc()
)。愛(ài)掏網(wǎng) - it200.com
JavaScript
- 避免頻繁操作樣式,最好一次性重寫
style
屬性,或者將樣式列表定義為class
并一次性更改class
屬性。愛(ài)掏網(wǎng) - it200.com - 避免頻繁操作
DOM
,創(chuàng)建一個(gè)documentFragment
,在它上面應(yīng)用所有DOM
操作,最后再把它添加到文檔中。愛(ài)掏網(wǎng) - it200.com - 也可以先為元素設(shè)置
display: none
,操作結(jié)束后再把它顯示出來(lái)。愛(ài)掏網(wǎng) - it200.com因?yàn)樵?code>display屬性為none
的元素上進(jìn)行的DOM
操作不會(huì)引發(fā)回流和重繪。愛(ài)掏網(wǎng) - it200.com - 避免頻繁讀取會(huì)引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用,就用一個(gè)變量緩存起來(lái)。愛(ài)掏網(wǎng) - it200.com
- 對(duì)具有復(fù)雜動(dòng)畫的元素使用絕對(duì)定位,使它脫離文檔流,否則會(huì)引起父元素及后續(xù)元素頻繁回流。愛(ài)掏網(wǎng) - it200.com
一般的思路就是遞歸解決,對(duì)不同的數(shù)據(jù)類型做不同的處理:
function deepCopy (obj) {
let result = {}
for (let key in obj) {
if (obj[key] instanceof Object || obj[key] instanceof Array) {
result[key] = deepCopy(obj[key])
} else {
result[key] = obj[key]
}
}
return result
}
復(fù)制代碼
這個(gè)只能復(fù)制內(nèi)部有數(shù)組、對(duì)象或其他基礎(chǔ)數(shù)據(jù)類型的對(duì)象,假如有一些像RegExp
、Date
這樣的復(fù)雜對(duì)象復(fù)制的結(jié)果就是一個(gè){}
,無(wú)法正確進(jìn)行復(fù)制,因?yàn)闆](méi)有對(duì)這些特殊對(duì)象進(jìn)行單獨(dú)的處理。愛(ài)掏網(wǎng) - it200.com若要參考對(duì)復(fù)雜對(duì)象進(jìn)行復(fù)制,可以參考lodash
中數(shù)組深復(fù)制方法_.cloneDeep()
的實(shí)現(xiàn)方案,下面這篇文章對(duì)數(shù)組深復(fù)制的方法進(jìn)行了詳細(xì)的解析,有一定參考價(jià)值:
jerryzou.com/posts/dive-…
另外如果要復(fù)制的對(duì)象數(shù)據(jù)結(jié)構(gòu)較為簡(jiǎn)單,沒(méi)有復(fù)雜對(duì)象的數(shù)據(jù),那么可以用最簡(jiǎn)便的方法:
let cloneResult = JSON.parse(JSON.stringify(targetObj))
復(fù)制代碼
此前轉(zhuǎn)載了一篇文章,對(duì)JavaScript運(yùn)算精度丟失的原因及解決方案都有比較詳細(xì)的說(shuō)明: blog.csdn.net/qq_35271556…
瀏覽器相關(guān)
加載過(guò)程:
- 瀏覽器根據(jù) DNS 服務(wù)器解析得到域名的 IP 地址
- 向這個(gè) IP 的機(jī)器發(fā)送 HTTP 請(qǐng)求
- 服務(wù)器收到、處理并返回 HTTP 請(qǐng)求
- 瀏覽器得到返回內(nèi)容
渲染過(guò)程:
- 根據(jù) HTML 結(jié)構(gòu)生成 DOM 樹
- 根據(jù) CSS 生成 CSSOM
- 將 DOM 和 CSSOM 整合形成 RenderTree
- 根據(jù) RenderTree 開始渲染和展示
- 遇到
復(fù)制代碼
- 與
$parent
一樣,在父組件中可以通過(guò)訪問(wèn)this.$children
來(lái)訪問(wèn)組件的所有子組件實(shí)例。愛(ài)掏網(wǎng) - it200.com
非父子組件之間的數(shù)據(jù)傳遞
-
對(duì)于非父子組件間,且具有復(fù)雜組件層級(jí)關(guān)系的情況,可以通過(guò)
Vuex
進(jìn)行組件間數(shù)據(jù)傳遞: vuex.vuejs.org/zh/ -
在
Vue 1.0
中常用的event bus
方式進(jìn)行的全局?jǐn)?shù)據(jù)傳遞,在Vue 2.0
中已經(jīng)被移除,官方文檔中有說(shuō)明:$dispatch
和$broadcast
已經(jīng)被棄用。愛(ài)掏網(wǎng) - it200.com請(qǐng)使用更多簡(jiǎn)明清晰的組件間通信和更好的狀態(tài)管理方案,如:Vuex
。愛(ài)掏網(wǎng) - it200.com
blog.seosiwei.com/detail/35 blog.seosiwei.com/detail/36 blog.seosiwei.com/detail/37
router.vuejs.org/zh-cn/advan…
作者:不肥的肥羊
鏈接:https://juejin.im/post/5b0562306fb9a07aaf3596c1
來(lái)源:掘金
著作權(quán)歸作者所有。愛(ài)掏網(wǎng) - it200.com商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。愛(ài)掏網(wǎng) - it200.com - 與