總括: 包含這三個月來碰到的一些以爲比較好的面試題,三個月沒怎麼寫博客着實有些手癢,哈哈哈。7000餘字,不成敬意2333javascript
原文地址:個人前端進階之路php
博主博客地址:Damonare的我的博客html
烈火試真金,逆境試強者前端
相同點:vue
React native
,Vue的weex
不一樣點:html5
社區:React社區仍是要比vue大不少;java
開發模式:React在view層侵入性仍是要比Vue大不少的,React嚴格上只針對MVC的view層,Vue則是MVVM模式的一種實現;node
數據綁定:Vue有實現了雙向數據綁定,React數據流動是單向的react
數據渲染:對於大規模數據渲染,React要比Vue更快,渲染機制啓動時候要作的工做比較多;
數據更新方面:Vue 因爲採用依賴追蹤,默認就是優化狀態:你動了多少數據,就觸發多少更新,很少也很多。React在複雜的應用裏有兩個選擇:
(1). 手動添加 shouldComponentUpdate 來避免不須要的 vdom re-render。 (2).Components 儘量都用 pureRenderMixin,而後採用 redux 結構 + Immutable.js;
開發風格的偏好:React 推薦的作法是 JSX + inline style,也就是把 HTML 和 CSS 全都寫進 JavaScript 了,即"all in js";Vue進階以後推薦的是使用 webpack + vue-loader 的單文件組件格式,即html,css,js寫在同一個文件;
使用場景:React配合Redux架構適合超大規模多人協做的複雜項目;Vue則適合小快靈的項目。對於須要對 DOM 進行不少自定義操做的項目,Vue 的靈活性優於 React;
Vue要比React更好上手,具體可能體如今不少人不熟悉React的JSX語法和函數式編程的思想,以及想要發揮出React的最大威力須要學習它一系列生態的緣故;
Vue着重提升開發效率,讓前端程序員更快速方便的開發應用。React着重於變革開發思想,提高前端程序員編程的深度與創造力,讓前端工程師成爲真正的程序員而不是UI的構建者;
Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過時時間前瀏覽器能夠直接從瀏覽器緩存取數據,而無需再次請求。Expires 是HTTP 1.0的東西,如今默認瀏覽器均默認使用HTTP 1.1,因此它的做用基本忽略。
Cache-Control與Expires的做用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器緩讀取數據仍是從新發請求到服務器取數據。只不過Cache-Control的選擇更多,設置更細緻,若是同時設置的話,其優先級高於Expires。
以上是設置緩存時間的兩種方法。那麼當緩存時間過了咋整呢?有人確定說了,那就再次發起請求啊,這是對的。問題是若是服務器資源並無更新呢?好比說我有一個jQuery.js
文件已經緩存了,當它的緩存時間到了以後服務器的jQuery.js
文件也沒有更新,那實際上咱們直接使用本地緩存的文件就能夠啊!不必浪費帶寬和時間去從新請求一個新的文件啊!這時候咱們就須要再進一步看一下HTTP協議裏這幾個參數的做用了。
首先Last-Modified/If-Modified-Since要配合Cache-Control使用。
Etag/If-None-Match也要配合Cache-Control使用。
HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:
Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的惟一標識符,可以更加準確的控制緩存。Last-Modified與ETag是能夠一塊兒使用的,服務器會優先驗證ETag,一致的狀況下,纔會繼續比對Last-Modified,最後才決定是否返回304。
Ajax的狀態值
0: (未初始化)尚未調用send()方法。
1: (載入)已經調用send()方法,正在派發請求。
2: (載入完成)send()已經執行完成,已經接收到所有的響應內容。
3: (交互)正在解析響應內容。
4: (完成)響應內容已經解析完成,用戶能夠調用。
HTTP狀態碼
200 & OK: 請求成功;
204 & No Content: 請求處理成功,但沒有資源能夠返回;
206 & Partial Content: 對資源某一部分進行請求(好比對於只加載了通常的圖片剩餘部分的請求);
301 & Move Permanently: 永久性重定向;
302 & Found: 臨時性重定向;
303 & See Other: 請求資源存在另外一個URI,應使用get方法請求;
304 & Not Modified: 服務器判斷本地緩存未更新,能夠直接使用本地的緩存;
307 & Temporary Redirect: 臨時重定向;
400 & Bad Request: 請求報文存在語法錯誤;
401 & Unauthorized: 請求須要經過HTTP認證;
403 & Forbidden: 請求資源被服務器拒絕,訪問權限的問題;
404 & Not Found: 服務器上沒有請求的資源;
500 & Internal Server Error: 服務器執行請求時出現錯誤;
502 & Bad Gateway: 錯誤的網關;
503 & Service Unavailable: 服務器超載或正在維護,沒法處理請求;
504 & Gateway timeout: 網關超時;
1.History
createHashHistory
createBrowserHistory
createMemoryHistory
內部createHistory
實現:
// 內部的抽象實現 function createHistory(options={}) { ... return { listenBefore, // 內部的hook機制,能夠在location發生變化前執行某些行爲,AOP的實現 listen, // location發生改變時觸發回調 transitionTo, // 執行location的改變 push, // 改變location replace, go, goBack, goForward, createKey, // 建立location的key,用於惟一標示該location,是隨機生成的 createPath, createHref, createLocation, // 建立location } }
createLocation
方法:
function createLocation() { return { pathname, // url的基本路徑 search, // 查詢字段 hash, // url中的hash值 state, // url對應的state字段 action, // 分爲push、replace、pop三種 key // 生成方法爲: Math.random().toString(36).substr(2, length) } }
三種方法各自執行URL
前進的方式:
createBrowserHistory
: pushState、replaceStatecreateHashHistory
: location.hash=***
location.replace()
createMemoryHistory
: 在內存中進行歷史記錄的存儲僞代碼實現:
// createBrowserHistory(HTML5)中的前進實現 function finishTransition(location) { ... const historyState = { key }; ... if (location.action === 'PUSH') ) { window.history.pushState(historyState, null, path); } else { window.history.replaceState(historyState, null, path) } } // createHashHistory的內部實現 function finishTransition(location) { ... if (location.action === 'PUSH') ) { window.location.hash = path; } else { window.location.replace( window.location.pathname + window.location.search + '#' + path ); } } // createMemoryHistory的內部實現 entries = []; function finishTransition(location) { ... switch (location.action) { case 'PUSH': entries.push(location); break; case 'REPLACE': entries[current] = location; break; } }
URL
對應Location
對象,而UI
是由react的 components
來決定的,這樣就轉變成location
與components
之間的同步問題。
每個對象都會在內部連接到另外一個對象(該對象的原型對象),該對象有一個原型prototype
,當訪問對象的屬性或是方法的時候,不只僅會在原對象上查找,還會順着原型鏈在原型對象的原型鏈上查找,直到查到null
(全部原型鏈的頂層)爲止。原型是JavaScript實現繼承的基礎,new
關鍵字作的主要的事情就是將實例對象的__proto__
屬性指向原型對象的prototype。
閉包是javascript支持頭等函數的一種方式,它是一個可以引用其內部做用域變量(在本做用域第一次聲明的變量)的表達式,這個表達式能夠賦值給某個變量,能夠做爲參數傳遞給函數,也能夠做爲一個函數返回值返回。
閉包是函數開始執行的時候被分配的一個棧幀,在函數執行結束返回後仍不會被釋放(就好像一個棧幀被分配在堆裏而不是棧裏!)
閉包的應用:
var currying = function(fun) { //格式化arguments var args = Array.prototype.slice.call(arguments, 1); return function() { //收集全部的參數在同一個數組中,進行計算 var _args = args.concat(Array.prototype.slice.call(arguments)); return fun.apply(null, _args); }; }
const people = (num) => { var num = num; return { increase: () => { num++; }, get: () => { return num; } } } const man = people(4); man.increase(); man.get();
for (var i = 0; i < 4; i++) { (function(_i) { setTimeout(function() { console.log(_i) }, 1000) })(i) }
圖片懶加載的原理就是暫時不設置圖片的src
屬性,而是將圖片的url
隱藏起來,好比先寫在data-src
裏面,等某些事件觸發的時候(好比滾動到底部,點擊加載圖片)再將圖片真實的url
放進src
屬性裏面,從而實現圖片的延遲加載
圖片預加載,是指在一些須要展現大量圖片的網站,實現圖片的提早加載。從而提高用戶體驗。經常使用的方式有兩種,一種是隱藏在css的background的url屬性裏面,一種是經過javascript的Image對象設置實例對象的src屬性實現圖片的預加載。相關代碼以下:
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; } #preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; } #preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
function preloadImg(url) { var img = new Image(); img.src = url; if(img.complete) { //接下來可使用圖片了 //do something here } else { img.onload = function() { //接下來可使用圖片了 //do something here }; } }
跨域的方式有不少種,最經常使用的是jsonp
主要利用了script
的開放策略:經過script標籤引入一個js或者是一個其餘後綴形式(如php,jsp等)的文件,此文件返回一個js函數的調用。缺點在於只支持get請求並且存在安全問題。
CORS跨域,關鍵在於服務器,若是服務器實現了CORS跨域的接口,那麼就可使用ajax(請求路徑爲絕對路徑)進行跨域請求。CORS請求分爲兩種,一種是簡單請求,一種是非簡單請求。簡單請求是指請求方法在HEAD
,GET
,POST
三者之間而且請求頭信息侷限在
Content-Language
Content-Type:只限於三個值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
非簡單請求請求頭:
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段
執行簡單請求的時候,瀏覽器會在請求頭信息增長origin
字段,服務器據此來判斷請求域名是否在許可範圍以內,來決定是否返回Access-Control-Allow-Origin
字段。響應頭有如下幾種:
(1)Access-Control-Allow-Origin
該字段是必須的。它的值要麼是請求時Origin
字段的值,要麼是一個*
,表示接受任意域名的請求。
(2)Access-Control-Allow-Credentials
該字段可選。它的值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true
,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器。這個值也只能設爲true
,若是服務器不要瀏覽器發送Cookie,刪除該字段便可。
(3)Access-Control-Expose-Headers
該字段可選。CORS請求時,XMLHttpRequest
對象的getResponseHeader()
方法只能拿到6個基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。若是想拿到其餘字段,就必須在Access-Control-Expose-Headers
裏面指定。
(4)Access-Control-Max-Age
Access-Control-Max-Age
首部字段指明瞭預檢請求的響應的有效時間。
(5)Access-Control-Allow-Methods
Access-Control-Allow-Methods
首部字段用於預檢請求的響應。其指明瞭實際請求所容許使用的 HTTP 方法。
(6)Access-Control-Allow-Headers
Access-Control-Allow-Headers
首部字段用於預檢請求的響應。其指明瞭實際請求中容許攜帶的首部字段。
其餘方法:document.domin
,html5的postMessage
,window.name
等
函數節流讓指函數有規律的進行調用,應用場景:window.resize,遊戲中子彈發射(1s只能發射一顆子彈)等;
函數防抖讓函數在"調用''以後的一段時間後生效,應用場景:輸入框(例:在用戶中止輸入的500ms後再處理用戶數據)。
//函數節流 /* * @params {Function} fun 調用函數 * @params {delay} number 延遲時間 */ const throttle = (fun, delay, ...rest) => { let last = null; return () => { const now = + new Date(); if (now - last > delay) { fun(rest); last = now; } } } //實例 const throttleExample = throttle(() => console.log(1), 1000); //調用 throttleExample(); throttleExample(); throttleExample(); //函數防抖 const debouce = (fun, delay, ...rest) => { let timer = null; return () => { clearTimeout(timer); timer = setTimeout(() => { fun(rest); }, delay); } } //實例 const debouceExample = debouce(() => console.log(1), 1000); //調用 debouceExample(); debouceExample(); debouceExample();
時間複雜度平均狀況:O(n\log n) 最快:O(n^{2}) 空間複雜度: O(\log n)
var quickSort = function(arr) { console.time('2.快速排序耗時'); if (arr.length <= 1) { return arr; } var pivot = arr.splice(0, 1)[0]; var left = []; var right = []; for (var i = 0; i < arr.length; i++){ if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } console.timeEnd('2.快速排序耗時'); return quickSort(left).concat([pivot], quickSort(right)); }; var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]; console.log(quickSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。
CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。
易出現泄露的場景
JavaScript垃圾回收機制
此算法把「對象是否再也不須要」簡化定義爲「對象有沒有其餘對象引用到它」。若是沒有引用指向該對象(零引用),對象將被垃圾回收機制回收。
限制:沒法處理循環引用。在下面的例子中,兩個對象被建立,並互相引用,造成了一個循環。它們被調用以後不會離開函數做用域,因此它們已經沒有用了,能夠被回收了。然而,引用計數算法考慮到它們互相都有至少一次引用,因此它們不會被回收。
當變量進入環境時,例如,在函數中聲明一個變量,就將這個變量標記爲「進入環境」。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。而當變量離開環境時,則將其標記爲「離開環境」。
垃圾回收器在運行的時候會給存儲在內存中的全部變量都加上標記(固然,可使用任何標記方式)。而後,它會去掉環境中的變量以及被環境中的變量引用的變量的標記(閉包)。而在此以後再被加上標記的變量將被視爲準備刪除的變量,緣由是環境中的變量已經沒法訪問到這些變量了。最後,垃圾回收器完成內存清除工做,銷燬那些帶標記的值並回收它們所佔用的內存空間。
所謂的柯里化函數簡單的說就是將原本接受多個參數的函數變爲只接受一個參數的函數。柯里化函數的模板和實例以下:
var currying = function(fun) { //格式化arguments var args = Array.prototype.slice.call(arguments, 1); return function() { //收集全部的參數在同一個數組中,進行計算 var _args = args.concat(Array.prototype.slice.call(arguments)); return fun.apply(null, _args); }; } var add = currying(function() { var args = Array.prototype.slice.call(arguments); return args.reduce(function(a, b) { return a + b; }); }) add(1, 2, 4) /* * 經典面試題 * 函數參數不定回調函數數目不定 * 編寫函數實現: * add(1,2,3,4,5)==15 * add(1,2)(3,4)(5)==15 */ function add() { // 第一次執行時,定義一個數組專門用來存儲全部的參數 var _args = [].slice.call(arguments); // 在內部聲明一個函數,利用閉包的特性保存_args並收集全部的參數值 var adder = function () { var _adder = function() { [].push.apply(_args, [].slice.call(arguments)); return _adder; }; // 利用隱式轉換的特性,當最後執行時隱式轉換,並計算最終的值返回 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); } return _adder; } return adder.apply(null, _args); } // 輸出結果,可自由組合的參數 console.log(add(1, 2, 3, 4, 5)); // 15 console.log(add(1, 2, 3, 4)(5)); // 15 console.log(add(1)(2)(3)(4)(5)); // 15
題目:
import React from 'react' class App extends React.Component { constructor() { super(); this.state = { value: 0 } } componentDidMount() { this.setState({value: this.state.value + 1}); console.log(this.state.value); this.setState({value: this.state.value + 1}); console.log(this.state.value); this.setState({value: this.state.value + 1}); console.log(this.state.value); setTimeout(() => { this.setState({value: this.state.value + 1}); console.log(this.state.value); this.setState({value: this.state.value + 1}); console.log(this.state.value); }, 0) } }
答案: 0、0、0、二、3;
分析:
當setState
方法調用的時候React
就會從新調用render
方法來從新渲染組件;setState
經過一個隊列來更新state
,當調用setState
方法的時候會將須要更新的state放入這個狀態隊列中,這個隊列會高效的批量更新state
;
源碼地址:enqueueUpdate
function enqueueUpdate(component) { ensureInjected(); //判斷是否處於批量更新模式 if (!batchingStrategy.isBatchingUpdates) { //關鍵!下面的代碼片斷是這個方法的源碼 batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } //若是處於批量更新模式,則將這個組件保存在dirtyComponents dirtyComponents.push(component); }
源碼地址:ReactDefaultBatchingStrategy
//batchingStrategy對象 var ReactDefaultBatchingStrategy = { //注意默認爲false isBatchingUpdates: false, batchedUpdates: function(callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; if (alreadyBatchingUpdates) { callback(a, b, c, d, e); } else { //關鍵!!!事務的理解 transaction.perform(callback, null, a, b, c, d, e); } }, };
源碼地址:Transaction
如圖:事務會將所須要執行的方法(圖中的anyMethod)使用wrapper
封裝起來,再經過perform
方法執行該方法,但在perform
執行以前會先執行全部wrapper
中的initialize
方法,perform
方法執行結束後,再執行全部的close
方法;
var Transaction = require('./Transaction'); // 咱們本身定義的 var MyTransaction = function() { //do something }; Object.assign(MyTransaction.prototype, Transaction.Mixin, { //須要自定義一個getTransactionWrappers對象,獲取全部須要封裝的initialize方法和close方法 getTransactionWrappers: function() { return [{ initialize: function() { console.log('before method perform'); }, close: function() { console.log('after method perform'); } }]; }; }); //實例化一個transaction var transaction = new MyTransaction(); //須要調用的方法 var testMethod = function() { console.log('test'); } transaction.perform(testMethod); //before method perform //test //after method perform
理解題目的關鍵是,整個組件渲染到DOM中的過程就已經處於一次大的事務中了,所以在componentDidMount
方法中調用setState
的時候ReactDefaultBatchingStrategy.isBatchingUpdates = true;
這句代碼已經執行過了,因此setState
的結果並無當即生效,而是扔進了dirtyComponent
;所以執行三次setState的結果this.state.value的值依然是0,而setTimeout中的兩次setState因爲沒有調用過batchedUpdates
方法(isBatchingUpdates
默認爲false
),因此setState
方法當即生效,第二次setSState
同理
XSS是一種跨站腳本攻擊,是屬於代碼注入的一種,攻擊者經過將代碼注入網頁中,其餘用戶看到會受到影響(代碼內容有請求外部服務器);
CSRF是一種跨站請求僞造,冒充用戶發起請求,完成一些違背用戶請求的行爲(刪帖,改密碼,發郵件,發帖等)
防護方法舉例:
時隔三個月,終於迎來了博文的更新,有看到博友在評論留言:
內心很溫暖,這篇不算博文的博文就當是迴歸之做吧,接下來的時間會盡可能保持在一週一更,實習結束有的是時間了,哈哈哈。