2020,前端面試都問了啥?(面試官角度分享)

圖片

01javascript





javascript做用域與預解析html

什麼是預解析?前端

分兩步執行:java

第一步:(代碼尚未執行。預覽頁面以前,寫完以後)node

找程序中var關鍵字,若是找到了提早給var定義的變量賦值undefinedwebpack

找程序中的普通函數,若是找到了,函數提高,將整個函數賦值給函數名。web

若是找的var的名字和函數名字相同,函數優先。面試

第二步: 逐行解析代碼。按照上下順序。若是碰到函數定義,忽略。ajax

重點:函數內部一樣適用於js預解析。json


咱們經過幾道面試題,來了解下做用域和和預解析的原理

圖片

猜一猜此題中輸出的結果是?可能並非你想的結果,why?


代碼分析以下:

1-5行定義函數fun

6行定義變量n

7行執行函數並傳入變量n

注意:fun函數內部有預解析。


預解析及執行步驟:

1. Fun函數開始執行前,將var n提早執行,初始化爲undefined。

2. 因爲函數傳入參數n並無使用,忽略。

3. 開始執行第2行,輸出爲undefined。

4. 執行第3行,此時即n = 456,即將n值重置爲456。

5. 執行第4行,輸出改變後的n。

經過以上步驟分析,便可得知預解析的原理了(針對有var的變量提早賦初始值)


下面再看一題,看看函數預解析

圖片

猜一猜此題中輸出的結果是?可能並非你想的結果,why?


代碼分析以下:

29行定義一個全局變量

30-32行定義一個函數f1

33-36行定義一個函數f2

37行執行函數f2

38行輸出結果n

預解析及執行步驟:

1.代碼執行前,預解析先初始化變量n, f1, f2,將它們都置爲undefined.

2.接着執行第29行,爲變量n賦值

3.接着執行第30-32行,爲函數變量f1賦值,即f1爲函數了

4.接着執行第33-36行,爲函數變量f2賦值

5.執行第37行,即執行f2函數。

6.f2函數執行前,一樣預解析,先將n初始化爲undefined,接着把n賦值爲456,接着調用f1函數執行。

7.f1在f2中執行,那f1的做用域應該是f2,應該輸出456?

8.35行執行f1函數時無調用者,即f1函數爲全局做用域,輸出全局n爲123

9.第38行直接輸出全局變量n,即123


繼續深刻,再來一題:

圖片

猜一猜此題中輸出的結果是?可能並非你想的結果,why?


代碼分析以下:

預解析只針對var和function定義的變量


預解析及執行步驟:

1.預解析先初始化變量length, obj, f1並賦值爲undefined

2.接着爲變量length賦值爲100

3.接着爲函數變量f1賦值爲函數

4.接着爲變量obj賦值爲對象

5.第52行,調用對象obj的f2函數執行,傳入形參f1和1

6.第47行,f2函數接收實參爲f1, 接着執行f1函數

7.同上,f1函數執行無調用者,做用域爲全局,this指向window,輸出全局變量length,即100

8.第47行,f2函數接收實參f1,若要取到全部實參則須要arguments對象,第一個參數arguments[0]==f1,第二個arguments[1]==1,依此類推。

9.第49行,arguments[0]()看上去是f1(),那也應該輸出100?

10.注意arguments[0]做用域與f1的做用域並不相同,第48行直接執行f1,無調用者,做用域爲全局做用域,但arguments[0]做用域爲arguments對象,即this爲arguments,則應輸出2,由於arguments對象的屬性length爲2。




前端如何處理跨域

一、爲何會出現跨域問題

同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。能夠說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同源策略會阻止一個域的javascript腳本和另一個域的內容進行交互。所謂同源(即指在同一個域)就是兩個頁面具備相同的協議(protocol),主機(host)和端口號(port)。


二、什麼是跨域

當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不一樣即爲跨域。


當前頁面url

被請求頁面url

是否跨域

緣由

http://www.test.com/

http://www.test.com/index.html

同源

http://www.test.com/

https://www.test.com/index.html

跨域

協議不一樣(http/https

http://www.test.com/

http://www.baidu.com/

跨域

主域名不一樣(test/baidu

http://www.test.com/

http://blog.test.com

跨域

子域名不一樣(www/blog

http://www.test.com:8080/

http://www.test.com:7001

跨域

端口號不一樣


三、跨域解決方法

【1】設置document.domain解決沒法讀取非同源網頁的 Cookie問題

【2】跨文檔通訊 API:window.postMessage()

【3】JSONP

【4】CORS

【5】Proxy

做爲開發人員,最關心的跨域通常是2種交互的跨域,即Proxy和CORS,不少開發只圖一時方便,使用了Proxy,在打包後就發現又有跨域了,不知道怎麼解決,下面咱們經過實例一點點給你們解析。


跨域出現

首先,須要重現跨域,先用node寫一個簡單的接口,以下

圖片

使用命令node啓動這個服務,則搭建了一個最簡單的後端服務接口,而後使用前端ajax來請求這個接口,以下

圖片

建立一個簡單的html頁面,再加上上面的簡單ajax請求,在瀏覽器控制檯就看到了跨域error了

圖片

從日誌上看出現了「Access-Control-Allow-Origin」,表示是訪問源未被許可,即跨域了。


跨域解決之Proxy

如今項目通常都使用腳手架,即便用webpack,那可使用webpack自帶的proxy特性來處理跨域,下面咱們來配置一個簡單的webpack項目,以下


1.建立配置文件webpack.config.js

圖片

配置文件說明項目入口文件在src中index.js,打包輸出目錄爲dist,使用proxy處理跨域,即前端全部請求會自動跳轉到target指定的url

注意這裏有一個前綴,若沒有能夠不寫。


2.建立src目錄及index.js

圖片

3.建立工程依賴文件package.json

圖片

依賴文件中配置了webpack啓動命令

Npm run dev  啓動服務

Npm run start  啓動服務

Npm run build  打包命令

當啓動服務後,打開瀏覽器輸入 http://localhost:8080 ,便可看到一個空白頁面,打開控制檯能夠看到ajax請求

圖片

拿到交互的數據了。

這種方式是開發最經常使用的,可是打包後就有問題了,由於打包後就不存在proxy了,跨域仍是會存在,那應該怎麼解決?


跨域解決之CORS

這種方式是在後端配置,配置CORS後,前端無需任何處理便可訪問後端的接口,不管是在開發時仍是部署時都是OK的。

下面,咱們把proxy註釋掉,使用CORS方式處理,以下:

圖片

配置了cors後,接口就能夠隨便訪問了。

此時,還須要把前端請求地址改一下,改成直接請求後端接口,以下

圖片

刷新頁面,打開控制檯能夠看到請求地址爲

圖片 圖片

經過此種方式,在開發階段或部署都沒有問題,這也是開發中最經常使用的2種方式。

03





什麼是閉包?如何理解

閉包(closure)是javascript的一大難點,也是它的特點。不少高級應用都要依靠閉包來實現。

要理解閉包,首先要理解javascript的全局變量和局部變量。

javascript語言的特別之處就在於:函數內部能夠直接讀取全局變量,可是在函數外部沒法讀取函數內部的局部變量。

圖片

如何從外部讀取函數內部的局部變量?

咱們有時候須要獲取到函數內部的局部變量,正常狀況下,這是辦不到的!只有經過變通的方法才能實現。那就是在函數內部,再定義一個函數。


一、閉包的概念

上面代碼中的f2函數,就是閉包。

各類專業文獻的閉包定義都很是抽象,個人理解是: 閉包就是可以讀取其餘函數內部變量的函數。

因爲在javascript中,只有函數內部的子函數才能讀取局部變量,因此說,閉包能夠簡單理解成「定義在一個函數內部的函數「。

因此,在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。


二、閉包的用途

閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中,不會在f1調用後被自動清除。

爲何會這樣呢?緣由就在於f1是f2的父函數,而f2被賦給了一個全局變量,這致使f2始終在內存中,而f2的存在依賴於f1,所以f1也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。


在咱們平時的代碼中常常會用到閉包,好比在構造函數中

圖片

//另外一種寫法

圖片

三、常見閉包的寫法

圖片

另外一種調用方法

圖片

//定義函數並當即調用

圖片

四、閉包的實際應用

使用閉包,咱們能夠作不少事情。好比模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提高代碼的執行效率。


封裝

圖片

經過person.name是沒法獲取到name的值,若是要獲取到name的值能夠經過

Console.log(person.getName());   //直接獲取到 張三

person.setName("李四");     //從新設置新的名字

print(person.getName());      //獲取 李四


繼承

圖片

總結:閉包就是一個函數引用另一個函數的變量,由於變量被引用着因此不會被回收,所以能夠用來封裝一個私有變量。這是優勢也是缺點,沒必要要的閉包只會徒增內存消耗!