前端面試如何讓面試官對你刮目相看
2020年註定是不平凡的一年,找工作的競爭壓力可想而知,如何從眾多面試者中脫穎而出呢,總結了一波前端常見面試題,希望對大家有所幫助!
01-javascript作用域與預解析
什麼是預解析?
分兩步執行:
第一步:(代碼還沒有執行。預覽頁面之前,寫完之後)
找程序中var關鍵字,如果找到了提前給var定義的變量賦值undefined
找程序中的普通函數,如果找到了,函數提升,將整個函數賦值給函數名。
如果找的var的名字和函數名字相同,函數優先。
第二步: 逐行解析代碼。按照上下順序。如果碰到函數定義,忽略。
重點:函數內部同樣適用於js預解析。
我們通過幾道面試題,來了解下作用域和和預解析的原理
猜一猜此題中輸出的結果是?可能並不是你想的結果,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。
02-前端如何處理跨域
1、為什麼會出現跨域問題
同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同源策略會阻止一個域的javascript腳本和另外一個域的內容進行交互。所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)和端口號(port)。
2、什麼是跨域
當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不同即為跨域。
3、跨域解決方法
【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語言的特別之處就在於:函數內部可以直接讀取全局變量,但是在函數外部無法讀取函數內部的局部變量。
如何從外部讀取函數內部的局部變量?
我們有時候需要獲取到函數內部的局部變量,正常情況下,這是辦不到的!只有通過變通的方法才能實現。那就是在函數內部,再定義一個函數。
1、閉包的概念
上面代碼中的f2函數,就是閉包。
各種專業文獻的閉包定義都非常抽象,我的理解是: 閉包就是能夠讀取其他函數內部變量的函數。
由於在javascript中,只有函數內部的子函數才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個函數內部的函數“。
所以,在本質上,閉包是將函數內部和函數外部連接起來的橋樑。
2、閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中,不會在f1調用後被自動清除。
為什麼會這樣呢?原因就在於f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴於f1,因此f1也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。
在我們平時的代碼中經常會用到閉包,比如在構造函數中
//另一種寫法
3、常見閉包的寫法
另一種調用方法
//定義函數並立即調用
4、閉包的實際應用
使用閉包,我們可以做很多事情。比如模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率。
封裝
通過person.name是無法獲取到name的值,如果要獲取到name的值可以通過
Console.log(person.getName()); //直接獲取到 張三
person.setName("李四"); //重新設置新的名字
print(person.getName()); //獲取 李四
繼承
總結:閉包就是一個函數引用另外一個函數的變量,因為變量被引用著所以不會被回收,因此可以用來封裝一個私有變量。這是優點也是缺點,不必要的閉包只會徒增內存消耗!