在Promise內部,有一個狀態管理器的存在,有三種狀態: pending、fulfilled、rejectedjavascript
(1) promise初始化狀態爲pendinghtml
(2) 當前調用resolve(成功), 會由pending => fulfilledvue
(3) 當調用reject(失敗), 會由pending => rejectedjava
協議、端口和域名不一致致使的跨域 跨域是由於瀏覽器須要遵照同源策略,發出的請求即便相應成功,也被瀏覽器攔截下來node
同源策略限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互、這是一個用於隔離潛在惡意文件的重要安全機制、nginx
若是缺乏了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。面試
一、 防護 XSS 攻擊vuex
二、防護 CSRF 攻擊數據庫
一、經過jsonp跨域 二、document.domain + iframe跨域 三、location.hash + iframe 四、window.name + iframe跨域 五、postMessage跨域 六、跨域資源共享(CORS) 七、nginx代理跨域 八、nodejs中間代理跨域 九、WebSocket協議跨域json
jsonp的核心則是動態添加 script 標籤調用服務器提供的js腳本,容許用戶傳遞一個callback參數給服務器,而後服務器返回數據時會將這個callback參數做爲函數名老包裹JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了
一、Content方面
二、Server方面
三、Cookie方面
四、CSS方面
五、JavaScript
六、圖片方面
七、移動方面
大概流程
一、在瀏覽器數地址欄輸入URL
二、瀏覽器查看緩存,若是請求資源在緩存中而且新鮮,跳轉到轉碼步驟
Expires
和 Cache-Control
三、瀏覽器解析URL獲取協議,主機,端口,path
四、瀏覽器組裝一個HTTP(GET)請求報文
五、瀏覽器獲取主機ip地址,過程以下:
六、打開一個sokcet與目標地址,端口創建TCP連接, 三次握手以下:
七、TCP連接創建後發送HTTP請求
八、服務器接受請求並解析,將請求轉發到服務程序,如虛擬機使用HTTP Host頭部判斷請求的服務程序
九、服務器檢查HTTP請求頭是否包含緩存驗證信息若是驗證緩存新鮮,返回304等對應狀態碼
十、處理程序讀取完整請求並準備HTTP響應,可能須要查詢數據庫等操做
十一、服務器將響應報文經過TCP連接發送回瀏覽器
十二、瀏覽器接受HTTP響應,而後根據狀況選擇關閉TCP鏈接或者保留重用,關閉TCP鏈接的四次握手以下:
1三、瀏覽器檢查響應狀態碼:是否爲1XX、3XX、4XX、5XX,這些狀況處理與2XX不一樣
1四、若是資源可緩存,進行緩存
1五、對響應進行解碼(例如gzip壓縮)
1六、根據資源類型決定如何處理(假設資源爲HTML文檔)
1七、解析HTML文檔、構件DOM樹,下載資源,構造CSSOM樹,執行js腳本,這些操做沒有嚴格的前後順序,如下分別解釋
1八、構建DOM樹:
1九、解析過程當中遇到圖片、樣式表、js文件,啓動下載
20、構建CSSOM樹
2一、根據DOM樹和CSSOM樹構建渲染樹:
script,meta
這樣自己不可見的標籤。2)被CSS隱藏的節點,入display:none
2二、js解析以下
2三、顯示頁面(HTML解析過程當中會逐步顯示頁面)
一、經過meta標籤設置viewport,移動端的理想適口。
<meta name="viewport" content="width=width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
二、設置rem單位來進行適配、加上Flex佈局、百分比佈局
三、其它方案,響應式適配、vw+rem
JavaScript 函數中的 this 指向並非在函數定義的時候肯定的,而是在調用的時候肯定的。換句話說,函數的調用方式決定了 this 指向。 函數調用的方式
直接調用,就是經過 函數名(...) 這種方式調用
方法調用是指經過對象來調用其方法函數,它是 **對象.方法函數(...)** 這樣的調用形式
官方解釋:箭頭函數表達式的語法比函數表達式更簡潔,而且沒有本身的this,arguments,super或 new.target。
引用箭頭函數有兩個方面的做用:更簡短函數和而且不綁定this
箭頭函數不會建立this,它只會從本身的做用域鏈上一層繼承this。
簡而言之,箭頭函數,永遠指向當前調用的對象
BFC就是"塊級格式化上下文"的意思,建立了BFC的元素就是一個獨立的盒子,不過只有Block-level Box 能夠參與建立BFC,它規定了內部的Block-level Box如何佈局,而且與這個獨立盒子裏的佈局不受外部影響,固然它不會影響到外面的元素。
利用發佈/訂閱模式,發佈/訂閱模式由一個發佈者、多一個訂閱者以及一個調度中心所組成。訂閱者們先在調度中心訂閱某一事件並註冊相應的回調函數,當某一個時刻發佈者發佈了一個事件,調度中心取出訂閱了該事件的訂閱者們所註冊的回調函數來執行。
在發佈/訂閱模式中,訂閱者和發佈者並不須要關心對方的狀態,訂閱者只管訂閱事件並註冊回調、發佈者只管發佈事件,其他一切交給調度中心來調度,從而實現解耦。
Vue是採用數據劫持結合發佈者-訂閱者模式的方式,經過Object.defineProperty()來劫持各個屬性的setter、getter,在數據變更時發佈消息給訂閱者,觸發響應的監聽回調。
具體步驟:
第一步:須要 Observe 的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter 和 getter。這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就能監聽到數據變化。
第二步:Compile 解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖
第三步:Watcher 訂閱者是 Observe 和 Compile 之間通訊的橋樑,主要的事情是:
一、在自身實例化時往屬性訂閱器(dep)裏面添加本身
二、自身必須有一個update()
三、待屬性變更dep.notify()通知時,能調用自身的 update() 方法,並觸發 Compile 中綁定回調,則功成身退。
第四步:MVVM做爲數據綁定的入口,整合 Observe、Compile 和 Watcher 三者,經過 Observe 來監聽本身的 Model 數據變化。 經過 Compile 來解析編譯模板指令,最終利用 Watcher 搭起 Observe 和 Compile 之間的通訊橋樑; 達到數據變化 -> 視圖更新; 視圖交互(input) -> 數據 Model 變動的雙向綁定效果。
第一步:建立一個computedWathcers 空對象, 對 computed 對象遍歷,獲取計算屬性每個 userDef(自定義的函數或對象),而後嘗試獲取 userDef 的getter,而且爲每個 getter 添加一個watcher
第二步:判斷遍歷 computed 對象的key,是否已經存在 data 和 props 所佔用,存在則發出警告,不存在就調用 defineComputed 函數,給對應的key添加getter 和 setter
第三步:在調用 defineComputed 函數,會進行依賴收集 computedWatcher ,經過computedWatcher來進行派發通知,更新視圖
第四步:緩存就是在獲取 getter 數據的,判斷是否值相等,相等的話就直接返回,再也不進行更新視圖
MVVM分爲Model、View、ViewModel三者
Model 和 View 並沒有直接關聯,而是經過 ViewModel 來進行聯繫的, Model 和 ViewModel 之間有着雙向數據綁定的聯繫。所以當 Model 中的數據改變時會觸發 View 層的刷新,View 中因爲用戶交互操做而改變的數據也會在 Model 中同步
區別:這種模式實現了 Model 和 View的數據自動同步,所以開發時這須要要專一對數據的維護操做便可,而不須要本身操做dom 場景:數據操做比較多的場景,更加便捷
JS,是單線程的,利用JS的事件循環
事件循環大體分爲如下幾個步驟:
(1) 全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)
(2) 主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3) 一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。哪些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4) 主線程不斷重複上面的第三步
先執行宏觀任務,再執行微觀
for (macroTask of macroTaskQueue) {
// 1. Handle current MACRO-TASK
handleMacroTask();
// 2. Handle all MICRO-TASK
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
複製代碼
nextTick原理:
會有一個callbacks數組,接受nextTick的回調函數,push進去
首先判斷是否支持Promise,支持則利用的Promise.then進行調用遍歷調用callbacks數組
判斷是否支持 MutationObserver,支持則利用 MutationObserver 遍歷調用callbacks數組
判斷是否支持 setImmediate,支持則利用 setImmediate 遍歷調用callbacks數組
都不支持,則利用setTimeout進行遍歷調用 callbacks數組
面試回答 : 它的邏輯也很簡單,把傳入的回調函數 cb 壓入 callbacks 數組,最後一次性地根據 useMacroTask 條件執行 macroTimerFunc 或者是 microTimerFunc,而它們都會在下一個 tick 執行 flushCallbacks,flushCallbacks 的邏輯很是簡單,對 callbacks 遍歷,而後執行相應的回調函數。
VNode是對真實 DOM 的一種抽象描述,它的核心定義無非就幾個關鍵屬性,標籤名、數據、子節點、鍵值等,其它屬性都是用來擴展VNode的靈活性以及實現一些特殊 feature的。
Virtual DOM 除了它的數據結構的定義,映射到真實的 DOM 實際上要經歷 VNode的 create、diff、 patch等過程。
父子組件通訊,props、emit、ref調用函數
兄弟組件通訊,vuex、eventBus
vuex具備五種屬性: state、getter、mutation、action、module
vuex就是一個倉庫,倉庫裏面放不少對象。state就是數據存放地,對應於通常vue對象裏面的data
state裏面存放的數據是響應式的
getters能夠對state進行計算操做
能夠在多組件之間複用
action相似於mutation
action提價的是mutation,而是否是直接變動狀態
action能夠包含任何異步操做
可維護性會降低,你要想修改數據,你得維護三個地方
可讀性降低,由於一個組件裏的數據,你根本看不出來是從哪來的
增長耦合,大量的上傳派發,會讓耦合性大大的增長,原本Vue用Component就是爲了減小耦合,如今這麼用,和組件化的初衷相背。
總共分爲8個階段建立前/後,載入前/後,更新前/後,銷燬前/後
建立前/後: 在beforeCreated階段,vue實例的掛載元素el尚未。
載入前/後: 在beforeMount階段,vue實例的$el和data都初始化了,但仍是掛載以前爲虛擬的dom節點,data.message還未替換。在mounted階段,vue實例掛載完成,data.message成功渲染。
更新前/後: 當data變化時,會觸發beforeUpdate和updated方法。
銷燬前/後: 在執行destroy方法後,對data的改變不會觸發周期函數,說明此時vue實例已經解除了事件監聽以及和dom的綁定,可是dom結構依然存在
首先,組件能夠提高整個項目的開發效率。可以把頁面抽象成多個相對獨立的模快,解決了咱們傳統項目開發:效率低、難維護、複用性等問題。
而後,使用Vue.extend方法建立一個組件,而後使用Vue.component方法註冊組件。子組件須要數據,能夠在props中接受定義。而子組件修改好數據後,想把數據遞給父組件。能夠採用emit方法。
Proxy有多達13種攔截方法,不限於apply、ownKeys、deleteProperty、has等等是 **Object.defineProperty()**不具有的
Proxy返回的是一個新對象,咱們能夠只操做新的對象達到目的,而 Object.defineProperty 只能遍歷對象屬性直接修改
Proxy做爲新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利
固然,Proxy的劣勢就是兼容性問題,並且沒法用polyfill磨平,所以Vue的做者才聲明須要等到下個大版本(3.0)才能用Proxy重寫。
數據存儲方案:
大概說一下Cookie和localStorage、sessionStorage的功能特性。問到的話,Cookie的缺點就是,存儲量少、數據大影響性能、只能儲存字符串、安全性問題、須要檢查Cookie可否使用
/** * @desc 深拷貝,支持常見類型 * @param {Any} values * @return {Any} */
function deepClone(values) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == values || "object" != typeof values) return values;
// Handle Date
if (values instanceof Date) {
copy = new Date();
copy.setTime(values.getTime());
return copy;
}
// Handle Array
if (values instanceof Array) {
copy = [];
for (var i = 0, len = values.length; i < len; i++) {
copy[i] = deepClone(values[i]);
}
return copy;
}
// Handle Object
if (values instanceof Object) {
copy = {};
for (var attr in values) {
if (values.hasOwnProperty(attr)) copy[attr] = deepClone(values[attr]);
}
return copy;
}
throw new Error("Unable to copy values! Its type isn't supported.");
}
複製代碼
若是是一個數組,就聲明一個數據組,而後循環遍歷,遞歸賦值。 若是是一個對象,就聲明一個對象,而後判斷是否子元素,遞歸賦值
除了遞歸,咱們還能夠借用JSON對象的parse和stringify
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
複製代碼