文章轉(zhuǎn)載自公眾號(hào):前端工匠(微信號(hào):frontJS)
2020年注定是不平凡的一年,找工作的競爭壓力可想而知,如何從眾多面試者中脫穎而出呢,總結(jié)了一波前端常見面試題,希望對(duì)大家有所幫助!
1. javascript 作用域與預(yù)解析
什么是預(yù)解析?
分兩步執(zhí)行:
第一步:(代碼還沒有執(zhí)行。預(yù)覽頁面之前,寫完之后)
找程序中var
關(guān)鍵字,如果找到了提前給var
定義的變量賦值undefined
找程序中的普通函數(shù),如果找到了,函數(shù)提升,將整個(gè)函數(shù)賦值給函數(shù)名。
如果找的var
的名字和函數(shù)名字相同,函數(shù)優(yōu)先。
第二步: 逐行解析代碼。按照上下順序。如果碰到函數(shù)定義,忽略。
重點(diǎn):函數(shù)內(nèi)部同樣適用于js
預(yù)解析。
我們通過幾道面試題,來了解下作用域和和預(yù)解析的原理
function fun(){ console.log(n); var n = 456; console.log(n); } var n = 123; fun(n);
猜一猜此題中輸出的結(jié)果是?可能并不是你想的結(jié)果,why?
代碼分析如下:
1-5行定義函數(shù)fun
6行定義變量 n
7行執(zhí)行函數(shù)并傳入變量 n
注意:fun
函數(shù)內(nèi)部有預(yù)解析。
預(yù)解析及執(zhí)行步驟:
Fun
函數(shù)開始執(zhí)行前,將var n
提前執(zhí)行,初始化為undefined
。
- 由于函數(shù)傳入?yún)?shù) n 并沒有使用,忽略。
- 開始執(zhí)行第2行,輸出為
undefined
。
- 執(zhí)行第3行,此時(shí)即n = 456,即將n值重置為456。
- 執(zhí)行第4行,輸出改變后的n。
通過以上步驟分析,即可得知預(yù)解析的原理了(針對(duì)有var的變量提前賦初始值)
下面再看一題,看看函數(shù)預(yù)解析
猜一猜此題中輸出的結(jié)果是?可能并不是你想的結(jié)果,why?
代碼分析如下:
29行定義一個(gè)全局變量
30-32行定義一個(gè)函數(shù) f1
33-36行定義一個(gè)函數(shù) f2
37行執(zhí)行函數(shù) f2
38行輸出結(jié)果 n
預(yù)解析及執(zhí)行步驟:
1.代碼執(zhí)行前,預(yù)解析先初始化變量 n, f1, f2,將它們都置為 undefined
.
2.接著執(zhí)行第29行,為變量n賦值
3.接著執(zhí)行第30-32行,為函數(shù)變量 f1 賦值,即 f1 為函數(shù)了
4.接著執(zhí)行第33-36行,為函數(shù)變量 f2 賦值
5.執(zhí)行第37行,即執(zhí)行 f2 函數(shù)。
- f2 函數(shù)執(zhí)行前,同樣預(yù)解析,先將 n 初始化為
undefined
,接著把 n 賦值為456,接著調(diào)用f1函數(shù)執(zhí)行。
- f1 在 f2 中執(zhí)行,那 f1 的作用域應(yīng)該是 f2 ,應(yīng)該輸出456?
8.35行執(zhí)行 f1 函數(shù)時(shí)無調(diào)用者,即 f1 函數(shù)為全局作用域,輸出全局 n 為 123
9.第38行直接輸出全局變量 n ,即123
繼續(xù)深入,再來一題:
猜一猜此題中輸出的結(jié)果是?可能并不是你想的結(jié)果,why?
代碼分析如下:
預(yù)解析只針對(duì)var
和function
定義的變量
預(yù)解析及執(zhí)行步驟:
1.預(yù)解析先初始化變量length
, obj
, f1
并賦值為undefined
2.接著為變量length
賦值為 100
3.接著為函數(shù)變量 f1 賦值為函數(shù)
4.接著為變量`obj 賦值為對(duì)象
5.第52行,調(diào)用對(duì)象obj
的 f2 函數(shù)執(zhí)行,傳入形參 f1 和1
6.第47行,f2 函數(shù)接收實(shí)參為 f1 , 接著執(zhí)行 f1 函數(shù)
7.同上,f1 函數(shù)執(zhí)行無調(diào)用者,作用域?yàn)槿郑?code>this指向window
,輸出全局變量length
,即100
8.第47行,f2 函數(shù)接收實(shí)參 f1 ,若要取到所有實(shí)參則需要arguments
對(duì)象,第一個(gè)參數(shù)arguments[0]==f1
,第二個(gè)arguments[1]==1
,依此類推。
9.第49行,arguments[0]()
看上去是f1()
,那也應(yīng)該輸出100?
10.注意arguments[0]
作用域與f1的作用域并不相同,第48行直接執(zhí)行f1,無調(diào)用者,作用域?yàn)槿肿饔糜?,?code>arguments[0]作用域?yàn)?code>arguments對(duì)象,即this
為arguments
,則應(yīng)輸出 2,因?yàn)?code>arguments對(duì)象的屬性length
為2。
2. 前端如何處理跨域
1、為什么會(huì)出現(xiàn)跨域問題
同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會(huì)受到影響。可以說 Web 是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對(duì)同源策略的一種實(shí)現(xiàn)。同源策略會(huì)阻止一個(gè)域的 javascript 腳本和另外一個(gè)域的內(nèi)容進(jìn)行交互。所謂同源(即指在同一個(gè)域)就是兩個(gè)頁面具有相同的協(xié)議(protocol),主機(jī)(host)和端口號(hào)(port)。
2、什么是跨域
當(dāng)一個(gè)請(qǐng)求url的協(xié)議、域名、端口三者之間任意一個(gè)與當(dāng)前頁面url不同即為跨域。
當(dāng)前頁面url | 被請(qǐng)求頁面url | 是否跨域 | 原因 |
---|---|---|---|
http://m.hgci.cn/ | http://m.hgci.cn/index.html | 否 | 同源 |
http://m.hgci.cn/ | http://m.hgci.cn/index.html | 跨域 | 協(xié)議不同(http/https |
http://m.hgci.cn | http://www.baidu.cn/ | 跨域 | 主域名不同(w3cschool/baidu |
http://m.hgci.cn/ | http://123.w3cschool.cn | 跨域 | 子域名不同(www/123 |
www.test.com:8080/ | www.test.com:7001 | 跨域 | 端口號(hào)不同 |
3、跨域解決方法
【1】設(shè)置document.domain解決無法讀取非同源網(wǎng)頁的 Cookie問題
【2】跨文檔通信 API:window.postMessage()
【3】JSONP
【4】CORS
【5】Proxy
作為開發(fā)人員,最關(guān)心的跨域一般是2種交互的跨域,即Proxy
和CORS
,很多開發(fā)只圖一時(shí)方便,使用了Proxy
,在打包后就發(fā)現(xiàn)又有跨域了,不知道怎么解決,下面我們通過實(shí)例一點(diǎn)點(diǎn)給大家解析。
跨域出現(xiàn)
首先,需要重現(xiàn)跨域,先用node
寫一個(gè)簡單的接口,如下
使用命令node
啟動(dòng)這個(gè)服務(wù),則搭建了一個(gè)最簡單的后端服務(wù)接口,然后使用前端ajax
來請(qǐng)求這個(gè)接口,如下
創(chuàng)建一個(gè)簡單的html
頁面,再加上上面的簡單ajax
請(qǐng)求,在瀏覽器控制臺(tái)就看到了跨域error
了
從日志上看出現(xiàn)了“Access-Control-Allow-Origin”,表示是訪問源未被許可,即跨域了。
跨域解決之Proxy
現(xiàn)在項(xiàng)目一般都使用腳手架,即使用webpack
,那可以使用webpack
自帶的proxy
特性來處理跨域,下面我們來配置一個(gè)簡單的webpack
項(xiàng)目,如下
1.創(chuàng)建配置文件webpack.config.js
配置文件說明項(xiàng)目入口文件在src
中index.js
,打包輸出目錄為dist
,使用proxy
處理跨域,即前端所有請(qǐng)求會(huì)自動(dòng)跳轉(zhuǎn)到target
指定的url
注意這里有一個(gè)前綴,若沒有可以不寫。
2.創(chuàng)建src
目錄及index.js
3.創(chuàng)建工程依賴文件package.json
依賴文件中配置了webpack
啟動(dòng)命令
Npm run dev
啟動(dòng)服務(wù)
Npm run start
啟動(dòng)服務(wù)
Npm run build
打包命令
當(dāng)啟動(dòng)服務(wù)后,打開瀏覽器輸入 http://localhost:8080
,即可看到一個(gè)空白頁面,打開控制臺(tái)可以看到ajax
請(qǐng)求
拿到交互的數(shù)據(jù)了。
這種方式是開發(fā)最常用的,但是打包后就有問題了,因?yàn)榇虬缶筒淮嬖?code>proxy了,跨域還是會(huì)存在,那應(yīng)該怎么解決?
跨域解決之CORS
這種方式是在后端配置,配置CORS
后,前端無需任何處理即可訪問后端的接口,無論是在開發(fā)時(shí)還是部署時(shí)都是OK的。
下面,我們把proxy
注釋掉,使用CORS
方式處理,如下:
配置了cors
后,接口就可以隨便訪問了。
此時(shí),還需要把前端請(qǐng)求地址改一下,改為直接請(qǐng)求后端接口,如下
刷新頁面,打開控制臺(tái)可以看到請(qǐng)求地址為
通過此種方式,在開發(fā)階段或部署都沒有問題,這也是開發(fā)中最常用的2種方式。
3. 什么是閉包?如何理解
閉包(closure)是javascript
的一大難點(diǎn),也是它的特色。很多高級(jí)應(yīng)用都要依靠閉包來實(shí)現(xiàn)。
要理解閉包,首先要理解javascript
的全局變量和局部變量。
javascript
語言的特別之處就在于:函數(shù)內(nèi)部可以直接讀取全局變量,但是在函數(shù)外部無法讀取函數(shù)內(nèi)部的局部變量。
如何從外部讀取函數(shù)內(nèi)部的局部變量?
我們有時(shí)候需要獲取到函數(shù)內(nèi)部的局部變量,正常情況下,這是辦不到的!只有通過變通的方法才能實(shí)現(xiàn)。那就是在函數(shù)內(nèi)部,再定義一個(gè)函數(shù)。
1、閉包的概念
上面代碼中的 f2 函數(shù),就是閉包。
各種專業(yè)文獻(xiàn)的閉包定義都非常抽象,我的理解是: 閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
由于在javascript
中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)“。
所以,在本質(zhì)上,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁。
2、閉包的用途
閉包可以用在許多地方。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中,不會(huì)在 f1 調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢?原因就在于 f1 是 f2 的父函數(shù),而 f2 被賦給了一個(gè)全局變量,這導(dǎo)致 f2 始終在內(nèi)存中,而 f2 的存在依賴于 f1 ,因此 f1 也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
在我們平時(shí)的代碼中經(jīng)常會(huì)用到閉包,比如在構(gòu)造函數(shù)中
//另一種寫法
3、常見閉包的寫法
另一種調(diào)用方法
//定義函數(shù)并立即調(diào)用
4、閉包的實(shí)際應(yīng)用
使用閉包,我們可以做很多事情。比如模擬面向?qū)ο蟮拇a風(fēng)格;更優(yōu)雅,更簡潔的表達(dá)出代碼;在某些方面提升代碼的執(zhí)行效率。
封裝
通過person.name
是無法獲取到name
的值,如果要獲取到name
的值可以通過
Console.log(person.getName()); //直接獲取到 張三 person.setName("李四"); //重新設(shè)置新的名字 print(person.getName()); //獲取 李四
繼承
總結(jié):閉包就是一個(gè)函數(shù)引用另外一個(gè)函數(shù)的變量,因?yàn)樽兞勘灰弥圆粫?huì)被回收,因此可以用來封裝一個(gè)私有變量。這是優(yōu)點(diǎn)也是缺點(diǎn),不必要的閉包只會(huì)徒增內(nèi)存消耗!
以上就是W3Cschool編程獅
關(guān)于2020前端面試都會(huì)問啥?的相關(guān)介紹了,希望對(duì)大家有所幫助。