BAT 前端開發面經 —— 吐血總結 前端相關片斷整理——持續更新 前端基礎精簡總結 Web Storage You don't know js

BAT 前端開發面經 —— 吐血總結

 

 

更好閱讀,請移步這裏

聊以前

最近暑期實習招聘已經開始,我的目前參加了阿里的內推及騰訊和百度的實習生招聘,在此總結一下
一是備忘、總結提高,二是但願給你們一些參考
其餘面試及基礎相關能夠參考其餘博文:css

每位面試官的面試時間基本都在 40-80 分鐘,下面先簡要介紹各個面試流程,問題詳情見具體公司分類html

騰訊內推&校招 offer got前端

首先騰訊分爲三面,都爲技術面:vue

  • 初試一個面試官
  • 複試兩個面試官,包括電話遠程 online codinghtml5

    (我也不知道爲何幾天內三個面試官面我,只算兩個面試過程 ヾ(´A`)ノ゚)
  • 終面一位面試官java

    這位應該是大 boss ,他說並非前端開發的,面試的問題也不會很深刻,主要看我的對這個領域的想法以及對常見問題在前端領域的解決思路,不按套路出牌,emmm 感受回答的不理想
    相似這種問題:
    • 說說最能體現你能力的工做
    • 網絡安全的實現node

      如何檢測惡意腳本
      如何屏蔽
      ...react

騰訊二面 & online codingwebpack

  • 騰訊的遠程 online coding 時間較長,大概一個多小時,不只要按要求編程,也要口述思路以及關鍵點,過程相似壓力面,面試官說完題目要求會給你思考及coding 時間,他能夠遠程看到面試者的 coding 狀態,主要考察應變能力,思惟活躍度,編碼習慣,調試能力,以及測試方法,本人在此過程當中沒有太注意測試過程,致使對於特殊狀況考慮不全面,測試樣例不完善,望小夥伴們注意 ヾ(´A`)ノ゚,不過,在口述代碼時發現也能夠本身提出來須要完善的地方。

  • coding 爲一到兩題,後續問題都是圍繞它結合實際應用進行拓展,主要考察是否能靈活運用以及類似的思路轉換,當時面試時間太長以及基礎知識較差,進制轉換,存儲那些個基礎被小學老師收回,一連串的炮轟簡直爽歪歪,我的表示此過程本身的表現較差,也要重視基礎基礎啊老鐵們 ( ̄_ ̄ )

經歷了騰訊雲的四個面試官,以及其餘部門一個面試官的醬油麪,騰訊的技術面試官廣泛語速較快,思路轉換很快,要跟上面試官的節奏,聊到本身比較熟悉的能夠多說幾句,他們也會順着你回答的內容進行深刻,也會引導面試者的回答方向,若是不太熟儘可能坦白一些,不懂裝懂很容易 gg

才接到通知另加兩輪面試委員會現場技術面,簡直爽歪歪 ヾ(´A`)ノ

騰訊校招現場

  • 一面

    面試官很 nice,基本看着簡歷問的,話題圍繞項目(項目隔得時間過久,心裏寧願粗暴的技術面),基本爲大體的瞭解,包括平時的學習習慣和關注的技術發展
    • 問到了 ES5 ES6 ES7 甚至 ES8
    • 項目中用過vue, 就問了 react vue 相關
    • 實習中的工做方式,如何處理設計師的設計稿,回答了包括考慮響應式佈局的經常使用方案
      • sass--定義function,使用rem

        問是否有其餘負面做用

        想到了頁面閃爍,因爲初始化時須要計算頁面尺寸,可能有縮放

        如何避免:

        <!-- 若是在頁面加載完成後,頁面是用js動態添加的,這個問題就不太明顯, --> doc.addEventListener('DOMContentLoaded‘', function(e) { <!-- doc.body.style.fontSize = 12 * dpr + 'px'; 淘寶處理 --> }, false);
      • 媒體查詢

    • 移動端和pc端的工做
  • 二面

    面試官不是前端的,因此沒有問很深刻的問題,主要模擬實際場景考察實現思路以及先後端的方案,後端沒說出什麼,基本上只是就前端層面進行了描述,包括實現思路、須要注意的問題、以及性能和安全方面的考慮
    • 前端安全
    • 場景考察
      • 直播彈幕處理
      • 後端返回大量數據且包含運算,如何實時顯示
      • 。。。記不清了
  • HR 面
    • 介紹經歷
    • 家庭狀況
    • 職業規劃
    • 對騰訊的認識及與其餘實習單位對比
    • 對比實習過的企業
    • 是否還報了其餘企業
    • 男友工做找的怎麼樣了

阿里內推 二面 卒

我投的是螞蟻金服的前端開發,投過簡歷硬生生排隊等了12天,還被內推人提早告知螞蟻的前端很嚴格 ( ̄_ ̄ )
阿里分爲在線測試,初試 ......

  • 首先是在線測試

    投完簡歷後官網會有相關在線測試題
    阿里前端的在線測試只有一道coding 題,限時 30 分,因爲初次在線答題,看着倒計時緊張的思路不通,未能準確理解題意,但實際題目並不難,考察使用原生 js 實現相似css 的層級選擇器的功能,具體題目記不太清,將僅存的記憶寫了下來並附上我的實現,詳情見本文後部分
  • 一面
  • 二面
  • 掛掉了。。。

baidu 等通知

百度投的是核心搜索部門,但一面後估計不合適,沒有了消息,後簡歷轉到百度金融

  • 一面

    面試官語速很快,通常不給太多思考時間--------感受本身說話都打着節拍 ( ̄_ ̄ )

  • 二面

    面試官很和善,是惟一一個認真看了我博客的面試官,很榮幸,也很緊張

    基本偏向基礎
    但因爲沒安排好時間,面試時在戶外,聽不太清面試官的問題,在此提醒各位小夥伴提早選好面試場地,避免環境影響

  • 三面

    一樣和善的面試官,開始考察基礎知識,編譯原理,網絡協議等基礎,後面考察我的素質,後續更注重我的經歷,包括如何學習,找實習,實習中遇到的問題及如何解決,最驕傲的事情等等

關於結尾

  • 百度 & 阿里面試結束後都有問:你以爲在面試過程當中有什麼沒問到,但本身掌握比較好的技能麼

    面阿里時,頭腦發暈,回答:沒有,我感受您面的很專業,問的知識點都是比較核心的 ( ̄_ ̄ )
    百度經驗: 回答本身掌握還比較好的知識後,面試官真的會問不少問題(菜鳥能夠參考以上作法),若是面試官面試的較偏,倒能夠補充
  • 有什麼要問的
    • 騰訊二面教訓

      我:是否有導師帶,實習的大體安排以及部門業務等

      效果 

      面試官曰:

      你那麼關係有沒有導師的問題,但學習中最重要的是靠本身,導師並非負責回答你全部問題的
    • 百度經驗

      我:部門是否有按期交流分享的機會;工做中是按照職位仍是業務部門劃分,如何交流;偏向頁面仍是業務邏輯

      我的在自學過程當中發現前端體系太大,不知對於前端學習,您有什麼建議麼

      面試官:不少前端初學者都有相似的困惑,你最好本身從頭開始作一個項目,無論是本身瞎想的仍是模仿的,在項目過程當中去發現須要學習什麼技術,在遇到問題的時候才知道去哪一個方向發展和思考,只有在項目中才能持續的學習和提升,前端知識很碎,沒有項目很難有一連串的經歷

整體

整體來講,面試中有按照面試題出的,也有直接聊的,通常也會結合實際工做中會遇到的場景以及技術中的一些坑,回答時結合本身的項目經驗會更好,大廠的面試官更側重於面試者對深層原理的理解,對於實習生來講通常面基礎,若是有深查原理的習慣,我的的可塑造性也會較高

三廠體驗對比:

  • 騰訊阿里面試官一面開始都比較側重實踐,若是簡歷上有過實踐項目和實習經歷,會問更實際的問題,構造場景讓解答
  • 協議都會問到

  • 相對來講,百度更多基礎知識點,更多考察對更基本的知識掌握,不限於前端,還包括組成原理和編譯原理的一些知識,固然前端偏多(好比選取一個class 標籤元素有幾種方式等小細節的問題來考察,細到要把每一個表達說完整,把每一個單詞拼出來)

  • 阿里騰訊更側重應用中的注意事項(如:IE 和其餘瀏覽器中的事件處理機制)不太揪細節

  • 三廠都有問到算法,騰訊相對更注重對算法和邏輯,對面試者基礎知識要求較高,甚至涉及更底層的。

  • 另兩廠對算法,數據結構的要求都是瞭解階段

如下爲面試中的一些知識點以及我的的一些補充,敲黑板啦啦啦

1. Tencent

1.1. js 的事件機制

事件階段

通常的,事件分爲三個階段:捕獲階段、目標階段和冒泡階段。

  • 捕獲階段(Capture Phase)

    • 事件的第一個階段是捕獲階段。事件從文檔的根節點流向目標對象節點。途中通過各個層次的DOM節點,並在各節點上觸發捕獲事件,直到到達事件的目標節點。捕獲階段的主要任務是創建傳播路徑,在冒泡階段,事件會經過這個路徑回溯到文檔跟節點。
    • 或這樣描述:

      任何事件產生時,如點擊一個按鈕,將從最頂端的容器開始(通常是html的根節點)。瀏覽器會向下遍歷DOM樹直到找到觸發事件的元素,一旦瀏覽器找到該元素,事件流就進入事件目標階段

  • 目標階段(Target Phase)

    • 當事件到達目標節點的,事件就進入了目標階段。事件在目標節點上被觸發,而後會逆向迴流,直到傳播至最外層的文檔節點。
  • 冒泡階段(Bubble Phase)
    • 事件在目標元素上觸發後,並不在這個元素上終止。它會隨着DOM樹一層層向上冒泡,回溯到根節點。
    • 冒泡過程很是有用。它將咱們從對特定元素的事件監聽中釋放出來,若是沒有事件冒泡,咱們須要監聽不少不一樣的元素來確保捕獲到想要的事件

事件處理程序

  • DOM0 級事件處理程序

    var btn5 = document.getElementById('btn5'); btn5.onclick=function(){ console.log(this.id);//btn5 };
    • 基於 DOM0 的事件,對於同一個 dom 節點而言,只能註冊一個,後邊註冊的 同種事件 會覆蓋以前註冊的。

      利用這個原理咱們能夠解除事件,btn5.onclick=null;其中this就是綁定事件的那個元素;
    • 以這種方式添加的事件處理程序會在事件流的冒泡階段被處理;

  • DOM2 級事件處理程序
    • DOM2支持同一dom元素註冊多個同種事件,事件發生的順序按照添加的順序依次觸發(IE是相反的)。
    • DOM2事件經過 addEventListener 和 removeEventListener 管理

      // addEventListener(eventName,handlers,boolean);removeEventListener() // 兩個方法都同樣接收三個參數,第一個是要處理的事件名,第二個是事件處理程序, // 第三個值爲false時表示在事件冒泡階段調用事件處理程序,通常建議在冒泡階段使用, // 特殊狀況纔在捕獲階段; // 注意:經過addEventListener()添加的事件處理程序只能用removeEventListener()來移除 // 而且移除時傳入的參數必須與添加時傳入的參數同樣;好比 var btn2 = document.getElementById('btn2'); var handlers = function () { console.log(this.id); }; btn2.addEventListener('click',handlers,false); btn2.removeEventListener('click',handlers.false);
    • ie 事件處理程序

      //IE事件處理程序(IE和Opera支持) /* IE用了attachEvent(),detachEvent(),接收兩個參數,事件名稱和事件處理程序,  * 經過attachEvent()添加的事件處理程序都會被添加到冒泡階段,因此平時爲了兼容更多的瀏覽器最好將事件添加到事件冒泡階段,IE8及之前只支持事件冒泡;  */ var btn3 = document.getElementById('btn3'); var handlers2=function(){ console.log(this===window); // true,注意attachEvent()添加的事件處理程序運行在全局做用域; }; btn3.attachEvent('onclick',handlers2);
---ie 與其餘瀏覽器的區別

總結

DOM事件模型中的事件對象經常使用屬性:

  • type用於獲取事件類型
  • target獲取事件目標
  • stopPropagation()阻止事件冒泡
  • preventDefault()阻止事件默認行爲
  • 判斷加載狀態 —— onload 事件

IE事件模型中的事件對象經常使用屬性:

  • type用於獲取事件類型
  • srcElement獲取事件目標
  • cancelBubble 阻止事件冒泡
  • returnValue 阻止事件默認行爲
  • 經過 readystate 屬性值判斷什麼時候方法下載完畢可用

    readystate共有如下幾個值:
    • uninitialized: 對象存在但未初始化;
    • loading:對象正在加載;
    • loaded:對象數據加載完畢;
    • interactive:能夠操做對象了,但還沒加載完畢;
    • complete:加載完畢。

    注意上面5個值並不必定每一個事件都全包含,而且不必定是什麼順序。

    Document.readyState 屬性
    一個文檔的 readyState 能夠是如下之一:
    • loading / 加載

      document 仍在加載。
    • interactive / 互動

      文檔已經完成加載,文檔已被解析,可是諸如圖像,樣式表和框架之類的子資源仍在加載。
    • complete / 完成

      T文檔和全部子資源已完成加載。狀態表示 load 事件即將被觸發。

    當這個屬性的值變化時,document 對象上的readystatechange 事件將被觸發。

事件對象

  • IE

    IE中事件對象是做爲全局對象 window.event 存在的
  • Firefox

    Firefox中則是作爲句柄( handler )的第一個參數傳入
  • 通用

    var evt = window.event || arguments[0];

事件監聽

  • Chrome、FireFox、Opera、Safari、IE9.0及其以上版本

    addEventListener(eventName,handler,boolean); removeEventListener() /* 兩個方法都同樣接收三個參數, * 事件名 * 事件處理程序 * boolean false時表示在事件冒泡階段調用事件處理程序,通常建議在冒泡階段使用 */

  • IE8.0及其如下版本

    element.attachEvent(type, handler); element.detachEvent(type, handler); /* element 要綁定事件的對象,html 節點 * type 事件類型 +'on' 如: "onclick, onmouseover" * listener 事件處理程序(只寫函數名,不帶括號) */
  • 早期瀏覽器
    obj['on' + type] = handler

阻止冒泡

  • event.stopPropagation
  • event.cancelBubble = true //IE

阻止默認事件

  • event.preventDefault()
  • event.returnValue = false //IE
---通用的事件監聽器
// event(事件)工具集,來源:github.com/markyun markyun.Event = { // 頁面加載完成後 readyEvent: function (fn) { if (fn == null) { fn = document; } var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = fn; } else { window.onload = function () { oldonload(); fn(); }; } }, // 視能力分別使用dom0||dom2||IE方式 來綁定事件 // 參數: 操做的元素,事件名稱 ,事件處理程序 addEvent: function (element, type, handler) { if (element.addEventListener) { //事件類型、須要執行的函數、是否捕捉 element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, function () { handler.call(element); }); } else { element['on' + type] = handler; } }, // 移除事件 removeEvent: function (element, type, handler) { if (element.removeEnentListener) { element.removeEnentListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } }, // 阻止事件 (主要是事件冒泡,由於IE不支持事件捕獲) stopPropagation: function (ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, // 取消事件的默認行爲 preventDefault: function (event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, // 獲取事件目標 getTarget: function (event) { return event.target || event.srcElement; }, // 獲取event對象的引用,取到事件的全部信息,確保隨時能使用event; getEvent: function (e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; } };

1.2. vue

---Vue 生命週期
---雙向綁定原理 & 如何實現
---vuex 原理
---vue 數據更新後執行

1.3. 跨域

什麼叫跨域

方案

1.4. 安全 && 怎樣預防

1.5. session & cookie

1.6. 本地存儲

1.7. 瀏覽器緩存

1.8. 頁面從輸入URL 到加載過程

1.9. HTTP

---content-type
  • application/x-www-form-urlencoded

    最多見的 POST 提交數據的方式了。瀏覽器的原生 form 表單,若是不設置 enctype 屬性,那麼最終就會以 application/x-www-form-urlencoded方式提交數據。

    傳遞的key/val會通過URL轉碼,因此若是傳遞的參數存在中文或者特殊字符須要注意。

    //例子 //b=曹,a=1 POST HTTP/1.1(CRLF) Host: www.example.com(CRLF) Content-Type: application/x-www-form-urlencoded(CRLF) Cache-Control: no-cache(CRLF) (CRLF) b=%E6%9B%B9&a=1(CRLF) //這裏b參數的值"曹"由於URL轉碼變成其餘的字符串了
  • multipart/form-data

    常見的 POST 數據提交的方式。咱們使用表單上傳文件時,必須讓 form 的 enctyped 等於這個值
    而且Http協議會使用boundary來分割上傳的參數

  • text/xml

    <!-- 例子 --> POST http://www.example.com HTTP/1.1(CRLF) Content-Type: text/xml(CRLF) (CRLF) <?xml version="1.0"?> <resource> <id>123</id> <params> <name> <value>homeway</value> </name> <age> <value>22</value> </age> </params> </resource>
  • application/json

    用來告訴服務端消息主體是序列化後的 JSON 字符串

    //例子 //傳遞json POST HTTP/1.1(CRLF) Host: www.example.com(CRLF) Content-Type: application/json(CRLF) Cache-Control: no-cache(CRLF) Content-Length: 24(CRLF) (CRLF) { "a":1, "b":"hello" }

(CRLF)指 \r\n
參考: HTTP常見Content-Type比較

1.10. get & post

1.11. TCP & UDP & 握手

---TCP (Transmission Control Protocol)

兩個序號和三個標誌位:

  • 序號:seq序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記。
  • 確認序號:ack序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效,ack=seq+1。

標誌位:共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義以下:

  • URG:緊急指針(urgent poin* 有效。
  • ACK:acknowledgement 確認序號有效。
  • PSH:接收方應該儘快將這個報文交給應用層。
  • RST:reset 重置鏈接。
  • SYN:synchronous 創建聯機,發起一個新鏈接。
  • FIN:finish 釋放一個鏈接。

須要注意的是:

  • 不要將確認序號ack與標誌位中的ACK搞混了。
  • 確認方ack=發起方req+1,兩端配對。

  1. 第一次握手:
    Client將標誌位SYN置爲1,隨機產生一個值 seq=J,並將該數據包發送給Server
    Client進入SYN_SENT狀態,等待Server確認。
  2. 第二次握手:
    Server收到數據包後由標誌位SYN=1知道Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認鏈接請求,Server進入SYN_RCVD狀態。
  3. 第三次握手:
    Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,若是正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,若是正確則鏈接創建成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間能夠開始傳輸數據了

爲何須要確認

---四次揮手

因爲TCP鏈接時全雙工的,所以,每一個方向都必需要單獨進行關閉,這一原則是當一方完成數據發送任務後,發送一個FIN來終止這一方向的鏈接,收到一個FIN只是意味着這一方向上沒有數據流動了,即不會再收到數據了,可是在這個TCP鏈接上仍然可以發送數據,直到這一方向也發送了FIN

首先進行關閉的一方將執行主動關閉,而另外一方則執行被動關閉,上圖描述的便是如此。

  • 第一次揮手:
    Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
  • 第二次揮手:
    Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
  • 第三次揮手:
    Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
  • 第四次揮手:
    Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。

爲何TIME_WAIT狀態須要通過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

  • 可靠的實現 TCP 全雙工鏈接的終止
  • 容許老的重複分節在網絡中消失

MSL是Maximum Segment Lifetime英文的縮寫,中文能夠譯爲「報文最大生存時間」,他是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄,RFC 793中規定MSL爲2分鐘,實際應用中經常使用的是30秒,1分鐘和2分鐘等。2MSL即兩倍的MSL,TCP的TIME_WAIT狀態也稱爲2MSL等待狀態

爲何鏈接的時候是三次握手,關閉的時候倒是四次握手?

  • 由於當Server端收到Client端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。可是關閉鏈接時
  • 當Server端收到FIN報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端全部的報文都發送完了,我才能發送FIN報文,所以不能一塊兒發送。故須要四步握手。

參考: TCP三次握手詳解及釋放鏈接過程

1.12. HTTP 加密

---加密對象

對於HTTP協議來講,加密的對象有如下兩個:

  • 對通訊的加密:
    HTTP中沒有加密功能,可是能夠經過和SSL(Secure Socket Layer,安全套接層)組合使用,加密通訊內容。使用SSL創建安全通訊線路後,就能夠在這條線路上進行HTTP通訊了。與SSL組合使用的HTTP被稱爲HTTPS(HTTP Secure,超文本傳輸安全協議)。
  • 對通訊內容自己進行加密
    即對HTTP報文裏所包含的內容進行加密。這樣,首先客戶端要先對報文進行加密,而後再發給服務器。服務器在接受到請求時,須要對報文進行解密,再處理報文。該方式不一樣於SSL將整個通訊線路進行加密處理,因此內容仍然有被篡改的風險。
    • A、任何人均可以發起請求
      HTTP協議中,並未有確認通訊方這一步驟,因此,任何人均可以發送請求,而服務器在接受到任何請求時,都會作出相應的響應。
      • 解決方案:

        查明對手的證書

        雖然HTTP不能確認通訊方,但SSL是能夠的。SSL不只提供了加密處理,還使用了"證書"的手段,可用於確認通訊方。證書是由值得信賴的第三方機構頒佈,可用於肯定證實服務器和客戶端是實際存在的。因此,只要能確認通訊方持有的證書,便可判斷通訊方的真實意圖。
    • B、沒法判斷報文是否完整(報文可能已遭篡改)

      HTTP協議沒法判斷報文是否被篡改,在請求或者響應發出後,在對方接收以前,即便請求或者響應遭到篡改是沒法得知的。
      • 防止篡改:

        經常使用的,肯定報文完整性方法:MD五、SHA-1 等 散列值校驗方法,以及用來確認文件的數字簽名方法。可是,使用這些方法,也沒法百分百確保結果正確,由於MD5自己被修改的話,用戶是沒辦法意識到得。

    爲了有效防止這些弊端,能夠採用HTTPS。
  • POST 用戶安全登錄

    在關係到用戶隱私的時候,要時刻遵循兩個原則:
    • 任何應用程序都不能在本地存儲與安全相關的用戶信息
    • 任何應用程序在向服務器傳遞數據的時候,都不能直接傳遞與安全相關的用戶信息。

    要想讓用戶信息安全,就必須對其進行加密,讓別人即使是拿到了安全信息,擺在眼前的也是一串亂碼,沒有半點用處

    MD5是一種經常使用的加密方法,它是一種散列函數,利用MD5對用戶信息進行加密,會增長用戶信息安全性。

    網上有關於MD5的第三方框架Category

    利用這個第三方框架能夠實現對密碼進行MD5加密

    +隨機亂碼字符防止被破解

---加密算法

對稱加密

非對稱加密

---HTTPS 加密原理

HTTPS簡介

HTTPS實際上是有兩部分組成:HTTP + SSL / TLS,也就是在HTTP上又加了一層處理加密信息的模塊。服務端和客戶端的信息傳輸都會經過TLS進行加密,因此傳輸的數據都是加密後的數據

SSL協議是經過非對稱密鑰機制保證雙方身份認證,並完成創建鏈接,在實際數據通訊時經過對稱密鑰機制保障數據安全性

  • 服務器 用RSA生成公鑰和私鑰

  • 把公鑰放在證書裏發送給客戶端,私鑰本身保存

  • 客戶端首先向一個權威的服務器檢查證書的合法性,若是證書合法,客戶端產生一段隨機數,這個隨機數就做爲通訊的密鑰,咱們稱之爲對稱密鑰,用公鑰加密這段隨機數,而後發送到服務器

  • 服務器用密鑰解密獲取對稱密鑰,而後,雙方就已對稱密鑰進行加密解密通訊了

HTTPS 在傳輸數據以前須要客戶端(瀏覽器)與服務端(網站)之間進行一次握手,在握手過程當中將確立雙方加密傳輸數據的密碼信息。TLS/SSL 協議不只僅是一套加密傳輸的協議,更是一件通過藝術家精心設計的藝術品,TLS/SSL 中使用了非對稱加密,對稱加密以及 HASH 算法。握手過程的具體描述以下:

  1. 瀏覽器將本身支持的一套加密規則發送給網站。
  2. 網站從中選出一組加密算法與HASH算法,並將本身的身份信息以證書的形式發回給瀏覽器。證書裏面包含了網站地址,加密公鑰,以及證書的頒發機構等信息。
  3. 瀏覽器得到網站證書以後瀏覽器要作如下工做:

    a) 驗證證書的合法性(頒發證書的機構是否合法,證書中包含的網站地址是否與正在訪問的地址一致等),若是證書受信任,則瀏覽器欄裏面會顯示一個小鎖頭,不然會給出證書不受信的提示。

    b) 若是證書受信任,或者是用戶接受了不受信的證書,瀏覽器會生成一串隨機數的密碼,並用證書中提供的公鑰加密。

    c) 使用約定好的HASH算法計算握手消息,並使用生成的隨機數對消息進行加密,最後將以前生成的全部信息發送給網站。
  4. 網站接收瀏覽器發來的數據以後要作如下的操做:

    a) 使用本身的私鑰將信息解密取出密碼,使用密碼解密瀏覽器發來的握手消息,並驗證HASH是否與瀏覽器發來的一致。

    b) 使用密碼加密一段握手消息,發送給瀏覽器。
  5. 瀏覽器解密並計算握手消息的HASH,若是與服務端發來的HASH一致,此時握手過程結束,以後全部的通訊數據將由以前瀏覽器生成的隨機密碼並利用對稱加密算法進行加密。

參考:圖解HTTPS

如何保證 HTTP 傳輸安全性

  • 重要的數據,要加密

    好比用戶名密碼(若是簡單的md5,是能夠暴力破解),常見的是 md5(不可逆),aes(可逆),自由組合,還能夠加一些特殊字符

    舉例:username = aes(username), pwd = MD5(pwd + username)

  • 非重要數據,要簽名

    簽名的目的是爲了防止篡改,好比 http://www.xxx.com/getnews?id=1,獲取id爲1的新聞,若是不簽名那麼經過id=2,就能夠獲取2的內容等等。怎樣簽名呢?

    一般使用sign,好比原連接請求的時候加一個 sign 參數,sign=md5(id=1),服務器接受到請求,驗證sign是否等於 md5(id=1) ,若是等於說明正常請求。

    這會有個弊端,假如規則被發現,那麼就會被僞造,因此適當複雜一些,仍是可以提升安全性的。

  • 登陸態怎麼作

    http是無狀態的,也就是服務器無法本身判斷兩個請求是否有聯繫,那麼登陸以後,之後的接口怎麼斷定是否登陸呢

    簡單的作法,在數據庫中存一個token字段(名字隨意),當用戶調用登錄接口成功的時候,就將該字段設一個值,(好比aes(過時時間)),同時返回給前端,之後每次前端請求帶上該值,服務器首先校驗是否過時,其次校驗是否正確,不經過就讓其登錄。(redis 作這個很方便哦,key有過時時間)

來自:如何保證http傳輸安全性

1.13. 排序:冒泡,選擇,快速

1.14. 數據庫

---觸發器

一種特殊的存儲過程,存儲過程通常經過定義的名字直接調用,而觸發器是經過增、刪、改進行觸發執行的。會在事件發生時自動強制執行

觸發器是一種特殊的存儲過程,主要是經過事件來觸發而被執行的。它能夠強化約束,來維護數據的完整性和一致性,能夠跟蹤數據庫內的操做從而不容許未經許可的更新和變化。能夠聯級運算。如,某表上的觸發器上包含對另外一個表的數據操做,而該操做又會致使該表觸發器被觸發。

---事務 & 鎖
  • 事務

    就是被綁定在一塊兒做爲一個邏輯工做單元的SQL語句分組,若是任何一個語句操做失敗那麼整個操做就被失敗,之後操做就會回滾到操做前狀態,或者是上有個節點。

    爲了確保要麼執行,要麼不執行,就能夠使用事務。要將一組語句做爲事務考慮,就須要經過ACID測試,即原子性,一致性,隔離性和持久性。
  • 鎖:

    在全部的DBMS中,鎖是實現事務的關鍵,鎖能夠保證事務的完整性和併發性。與現實生活中鎖同樣,它能夠使某些數據的擁有者,在某段時間內不能使用某些數據或數據結構。固然鎖還分級別的。共享鎖(只讀不寫)、排他鎖(可讀可寫)

1.15. 軟件設計模式

設計原則

  • 對接口編程而不是對實現編程
  • 優先使用對象組合而不是繼承

---單例模式

單體是一個用來劃分命名空間並將一批相關的屬性和方法組織在一塊兒的對象,若是他能夠被實例化,那麼他只能被實例化一次

// 對象字面量 var Singleton = { attr1: 1, attr2: 2, method1: function(){ return this.attr1; }, method2: function(){ return this.attr2; } }; // 上面的全部成員變量都是經過Singleton來訪問的,可是它並非單體模式; // 由於單體模式還有一個更重要的特色,就是能夠僅被實例化一次,上面的只是不能被實例化的一個類,所以不是單體模式;對象字面量是用來建立單體模式的方法之一; /*要實現一個單體模式的話,咱們無非就是使用一個變量來標識該類是否被實例化 若是未被實例化的話,那麼咱們能夠實例化一次,不然的話,直接返回已經被實例化的對象 */ // 單體模式 var Singleton = function(name){ this.name = name; this.instance = null; }; Singleton.prototype.getName = function(){ return this.name; } // 獲取實例對象 function getInstance(name) { if(!this.instance) { this.instance = new Singleton(name); } return this.instance; } // 測試單體模式的實例 var a = getInstance("aa"); var b = getInstance("bb"); console.log(a === b) // true console.log(a.getName()) // aa console.log(b.getName()) // aa

應用案例

  • 彈窗

    傳統建立:好比我點擊一個元素須要建立一個div,我點擊第二個元素又會建立一次div,咱們頻繁的點擊某某元素,他們會頻繁的建立div的元素,雖然當咱們點擊關閉的時候能夠移除彈出代碼,可是呢咱們頻繁的建立和刪除並很差,特別對於性能會有很大的影響,對DOM頻繁的操做會引發重繪等,從而影響性能;所以這是很是很差的習慣;咱們如今能夠使用單體模式來實現彈窗效果,咱們只實例化一次就能夠

編寫通用的單體模式

咱們使用一個參數fn傳遞進去,若是有result這個實例的話,直接返回,不然的話,當前的getInstance函數調用fn這個函數,是this指針指向與這個fn這個函數;以後返回被保存在result裏面;如今咱們能夠傳遞一個函數進去,無論他是建立div也好,仍是建立iframe也好,總之若是是這種的話,均可以使用getInstance來獲取他們的實例對象;

// 建立div var createWindow = function(){ var div = document.createElement("div"); div.innerHTML = "我是彈窗內容"; div.style.display = 'none'; document.body.appendChild(div); return div; }; // 建立iframe var createIframe = function(){ var iframe = document.createElement("iframe"); document.body.appendChild(iframe); return iframe; }; // 獲取實例的封裝代碼 var getInstance = function(fn) { var result; return function(){ return result || (result = fn.call(this,arguments)); } }; // 測試建立div var createSingleDiv = getInstance(createWindow); document.getElementById("Id").onclick = function(){ var win = createSingleDiv(); win.style.display = "block"; }; // 測試建立iframe var createSingleIframe = getInstance(createIframe); document.getElementById("Id").onclick = function(){ var win = createSingleIframe(); win.src = "http://cnblogs.com"; };
---如下爲補充
---工廠模式

客戶類和工廠類分開。消費者任什麼時候候須要某種產品,只需向工廠請求便可。消費者無須修改就能夠接納新產品。

工廠模式是爲了解決多個相似對象聲明的問題;也就是爲了解決實列化對象產生重複的問題。

  • 優勢:能解決多個類似的問題。
  • 缺點:
    • 不能知道對象識別的問題(對象的類型不知道)。
    • 當產品修改時,工廠類也要作相應的修改。
function CreatePerson(name,age,sex) { var obj = new Object(); obj.name = name; obj.age = age; obj.sex = sex; obj.sayName = function(){ return this.name; } return obj; } var p1 = new CreatePerson("longen",'28','男'); var p2 = new CreatePerson("tugenhua",'27','女');
---模塊模式

模塊模式的思路是爲單體模式添加私有變量和私有方法可以減小全局變量的使用

prototype + constructor

---裝飾者模式

裝飾者(decorator)模式可以在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責(方法或屬性)。
與繼承相比,裝飾者是一種更輕便靈活的作法。

能夠動態的給某個對象添加額外的職責,而不會影響從這個類中派生的其它對象。

// ES7裝飾器 function isAnimal(target) { target.isAnimal = true return target } // 裝飾器 @isAnimal class Cat { // ... } console.log(Cat.isAnimal) // true // 做用於類屬性的裝飾器: function readonly(target, name, descriptor) { discriptor.writable = false return discriptor } class Cat { @readonly say() { console.log("meow ~") } } var kitty = new Cat() kitty.say = function() { console.log("woof !") } kitty.say() // meow ~
---觀察者模式(發佈-訂閱)

發佈---訂閱模式又叫觀察者模式,它定義了對象間的一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,當一個對象發生改變時,全部依賴於它的對象都將獲得通知

發佈訂閱模式的流程以下:

  1. 肯定誰是發佈者(好比個人博客)。
  2. 而後給發佈者添加一個緩存列表,用於存放回調函數來通知訂閱者。
  3. 發佈消息,發佈者須要遍歷這個緩存列表,依次觸發裏面存放的訂閱者回調函數。
  4. 退訂(好比不想再接收到這些訂閱的信息了,就能夠取消掉)

【實現事件模型】

即寫一個類或是一個模塊,有兩個函數,一個bind一個trigger,分別實現綁定事件和觸發事件,核心需求就是能夠對某一個事件名稱綁定多個事件響應函數,而後觸發這個事件名稱時,依次按綁定順序觸發相應的響應函數。

大體實現思路就是建立一個類或是匿名函數,在bind和trigger函數外層做用域建立一個字典對象,用於存儲註冊的事件及響應函數列表,bind時,若是字典沒有則建立一個,key是事件名稱,value是數組,裏面放着當前註冊的響應函數,若是字段中有,那麼就直接push到數組便可。trigger時調出來依次觸發事件響應函數便可

var Event = (function(){ var list = {}, listen, trigger, remove; listen = function(key,fn){ if(!list[key]) { list[key] = []; } list[key].push(fn); }; trigger = function(){ var key = Array.prototype.shift.call(arguments), fns = list[key]; if(!fns || fns.length === 0) { return false; } for(var i = 0, fn; fn = fns[i++];) { fn.apply(this,arguments); } }; remove = function(key,fn){ var fns = list[key]; if(!fns) { return false; } if(!fn) { fns && (fns.length = 0); }else { for(var i = fns.length - 1; i >= 0; i--){ var _fn = fns[i]; if(_fn === fn) { fns.splice(i,1); } } } }; return { listen: listen, trigger: trigger, remove: remove } })(); // 測試代碼以下: Event.listen("color",function(size) { console.log("尺碼爲:"+size); // 打印出尺碼爲42 }); Event.trigger("color",42);
---代理模式

代理是一個對象,它能夠用來控制對本體對象的訪問,它與本體對象實現了一樣的接口,代理對象會把全部的調用方法傳遞給本體對象的
本地對象注重的去執行頁面上的代碼,代理則控制本地對象什麼時候被實例化,什麼時候被使用

優勢:

  • 代理對象能夠代替本體被實例化,並使其能夠被遠程訪問;
  • 它還能夠把本體實例化推遲到真正須要的時候;對於實例化比較費時的本體對象,或者由於尺寸比較大以致於不用時不適於保存在內存中的本體,咱們能夠推遲實例化該對象;
// 先申明一個奶茶妹對象 var TeaAndMilkGirl = function(name) { this.name = name; }; // 這是京東ceo先生 var Ceo = function(girl) { this.girl = girl; // 送結婚禮物 給奶茶妹 this.sendMarriageRing = function(ring) { console.log("Hi " + this.girl.name + ", ceo送你一個禮物:" + ring); } }; // 京東ceo的經紀人是代理,來代替送 var ProxyObj = function(girl){ this.girl = girl; // 經紀人代理送禮物給奶茶妹 this.sendGift = function(gift) { // 代理模式負責本體對象實例化 (new Ceo(this.girl)).sendMarriageRing(gift); } }; // 初始化 var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹")); proxy.sendGift("結婚戒"); // Hi 奶茶妹, ceo送你一個禮物:結婚戒
  • TeaAndMilkGirl 是一個被送的對象(這裏是奶茶妹);
  • Ceo 是送禮物的對象,他保存了奶茶妹這個屬性,及有一個本身的特權方法sendMarriageRing 就是送禮物給奶茶妹這麼一個方法;
  • 而後呢他是想經過他的經紀人去把這件事完成,因而須要建立一個經濟人的代理模式,名字叫ProxyObj ;
  • 他的主要作的事情是,把ceo交給他的禮物送給ceo的情人,所以該對象一樣須要保存ceo情人的對象做爲本身的屬性,同時也須要一個特權方法sendGift ,該方法是送禮物,所以在該方法內能夠實例化本體對象,這裏的本體對象是ceo送花這件事情,所以須要實例化該本體對象後及調用本體對象的方法(sendMarriageRing).

理解使用虛擬代理實現圖片的預加載

在網頁開發中,圖片的預加載是一種比較經常使用的技術,若是直接給img標籤節點設置src屬性的話,若是圖片比較大的話,或者網速相對比較慢的話,那麼在圖片未加載完以前,圖片會有一段時間是空白的場景,這樣對於用戶體驗來說並很差,那麼這個時候咱們能夠在圖片未加載完以前咱們能夠使用一個loading加載圖片來做爲一個佔位符,來提示用戶該圖片正在加載,等圖片加載完後咱們能夠對該圖片直接進行賦值便可;下面咱們先不用代理模式來實現圖片的預加載的狀況下代碼以下:

// 不使用代理的預加載圖片函數以下 var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); var img = new Image(); img.onload = function(){ imgNode.src = this.src; }; return { setSrc: function(src) { imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif"; img.src = src; } } })(); // 調用方式 myImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");
//利用代理模式來編寫預加載圖片 var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); return { setSrc: function(src) { imgNode.src = src; } } })(); // 代理模式 var ProxyImage = (function(){ var img = new Image(); img.onload = function(){ myImage.setSrc(this.src); }; return { setSrc: function(src) { myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif"); img.src = src; } } })(); // 調用方式 ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

這種懶加載方法不用代理模式也是能夠實現的,只是用代理模式。咱們可讓 myImage 只作一件事,只負責將實際圖片加入到頁面中,而loading圖片交給ProxyImage去作。從而下降代碼的耦合度。由於當我不想用loading的時候,能夠直接調用myImage 方法。也便是說假如我門不須要代理對象的話,直接能夠換成本體對象調用該方法便可
對比

  • 不代理:不知足單一職責原則,代碼耦合度高
  • myimage 函數只負責一件事,其餘交給代理

優勢

  • 用戶能夠放心地請求代理,他們只關心是否能獲得想要的結果。假如我門不須要代理對象的話,直接能夠換成本體對象調用該方法便可。
  • 在任何使用本體對象的地方均可以替換成使用代理。

參考:Javascript設計模式詳解

1.2. Online Coding

js 實現兩個超大數相加
基礎:

  • JS 中全部的數字類型,實際存儲都是經過 8 字節 double 浮點型 表示的,並非可以精確表示範圍內的全部數
  • 大整數存儲(安全使用範圍)
    • 其餘語言 2^63 - 1
    • js Math.pow(2, 53) - 1

      //js 最大和最小安全值 Number.MAX_SAFE_INTEGER //9007199254740991 Number.MIN_SAFE_INTEGER //-9007199254740991
var largeNumberAdd = function(num1, num2) { var arr1 = num1.split(''), arr2 = num2.split(''), tem = '', num3 = 0, result = [] var longDiff = arr1.length - arr2.length if (longDiff > 0) { for (let i = 0; i < longDiff; i++) { arr2.unshift('0') } }else if (longDiff < 0) { for (let i = 0; i < Math.abs(longDiff); i++) { arr1.unshift('0') } } for (let i = arr1.length - 1; i >= 0; i--) { tem = parseInt(arr1[i]) + parseInt(arr2[i]) + num3 // check if tem > 10 if (tem >= 10) { num3 = 1 result.push((tem + '')[1]) }else { num3 = 0 result.push(tem) } } return result.reverse().join('') } // console.log(largeNumberAdd('11111','11111')) console.log(largeNumberAdd('00000000000000000000011111','333331999')) console.log(11111+333331999) // console.log(largeNumberAdd('3333333333333333333333333333333311111111111111111111111111111111111111','333333333333333331111111111111111111111111111166666666666666'))

js 每秒鐘的計算量
js 如何解析後臺返回的超大數據
前提:

  • js 用浮點數表示全部64位數字,全部達到 2^53 的能夠被精確表示,更大的數字都會被裁剪,——如何表示64位數字
    雖然js 可以解析進制數字表示64位數字,但底層的數字表示不支持 64 位
    在瀏覽器中執行如下代碼

    <html> <head> <script language="javascript"> function showPrecisionLimits() { document.getElementById("r50").innerHTML = 0x0004000000000001 - 0x0004000000000000; document.getElementById("r51").innerHTML = 0x0008000000000001 - 0x0008000000000000; document.getElementById("r52").innerHTML = 0x0010000000000001 - 0x0010000000000000; document.getElementById("r53").innerHTML = 0x0020000000000001 - 0x0020000000000000; document.getElementById("r54").innerHTML = 0x0040000000000001 - 0x0040000000000000; } </script> </head> <body onload="showPrecisionLimits()"> <p>(2^50+1) - (2^50) = <span id="r50"></span></p> <p>(2^51+1) - (2^51) = <span id="r51"></span></p> <p>(2^52+1) - (2^52) = <span id="r52"></span></p> <p>(2^53+1) - (2^53) = <span id="r53"></span></p> <p>(2^54+1) - (2^54) = <span id="r54"></span></p> </body> </html>

    在Firefox,Chrome和IE瀏覽器中,能夠看到,若是可以存儲64位數字,則如下減法結果皆爲1。而結果相反,能夠看到2 ^ 53 + 1和2 ^ 53 間的差別丟失

    2 ^ 50 + 1) - (2 ^ 50)= 1 (2 ^ 51 + 1) - (2 ^ 51)= 1 (2 ^ 52 + 1) - (2 ^ 52)= 1 (2 ^ 53 + 1) - (2 ^ 53)= 0 (2 ^ 54 + 1) - (2 ^ 54)= 0

    位運算
    所以,咱們能夠選擇用兩個 32 位的數字表示 64 位整數,而後進行按位與

    var a = [ 0x0000ffff, 0xffff0000 ]; var b = [ 0x00ffff00, 0x00ffff00 ]; var c = [ a[0] & b[0], a[1] & b[1] ]; document.body.innerHTML = c[0].toString(16) + ":" + c[1].toString(16); //結果 ff00:ff0000

網絡安全

前端網絡安全的實現

  • 保證http傳輸安全性

如何檢測惡意腳本
如何屏蔽

2. 阿里

一面

  1. 簡單介紹一下本身
  2. 問題
    • HTTP 相關
      HTTP 與 HTTPS 區別,HTTPS 原理,如何加密
    • 談談閉包
    • 談談做用域鏈
    • 經常使用的跨域方式
    • vue
      • 特色
      • 生命週期
      • vuex
        實現原理(事件綁定)-如何實現
      • 與 react 的不一樣
        應用場景
      • 如何實現雙向綁定
      • 如何通訊
  3. 其餘
    • 還會哪些語言
    • 還有什麼感受沒問到但掌握很好的
    • 是否還有其餘的問題
    • 有什麼問題想要問的

二面

  • react
    屢次調用 setstate 爲何不立刻渲染
  • 解析 json ,手寫 parse 函數
  • csrf 舉例
  • 獲取dom節點方式
  • 將給定節點子元素第一個和最後一個元素替換
  • 端口號的做用

端口

IP地址讓網絡上的兩個節點之間能夠創建點對點的鏈接

端口號則爲端到端的鏈接提供了可能 (程序間通信的接口)

IP協議是由TCP、UDP、ARP、ICMP等一系列子協議組成的。其中

  • TCP和UDP協議

    主要用來作傳輸數據使用的

    在TCP和UDP協議中,都有端口號的概念存在

  • 端口號的做用

    主要是區分服務類別和在同一時間進行多個會話
    • 服務類別

      • 舉例來講,有主機A須要對外提供FTP和WWW兩種服務,若是沒有端口號存在的話,這兩種服務是沒法區分的。

        實際上,當網絡上某主機B須要訪問A的FTP服務時,就要指定目的端口號爲21;
        當須要訪問A的WWW服務時,則須要將目的端口號設爲80,這時A根據B訪問的端口號,就能夠區分B的兩種不一樣請求
    • 多個會話
      • 主機A須要同時下載網絡上某FTP服務器B上的兩個文件,那麼A須要 與B同時創建兩個會話,而這兩個傳輸會話就是靠源端口號來區分的。在這種狀況下若是沒有源端口號的概念,那麼A就沒法區分B傳回的數據到底是屬於哪一個會話,屬於哪一個文件
      • 通訊過程是,A使用本機的1025號端口請求B的21號端口上的文件1,同時又使用1026號端口請求文件2。對於返回的數據,發現是傳回給1025號端口的,就認爲是屬於文件1;傳回給1026號端口的,則認爲是屬於文件2。這就是端口號區分多個會話的做用。

在線編程——編寫一個 css 層級選擇器

根據一個給定的元素生成一個css 選擇器,函數名爲genCssSelector ,
點擊某元素彈出該元素及其父元素,相似 querySelector

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Document</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script language="javaScript"> // your code here var genCssSelector = function (e) { e = e || window.event var tar = e.target || e.srcElement var objArr = [] while (tar) { if (tar.id) { objArr.push('#' + tar.id) console.log('id') return objArr.reverse().join(' ') // 考慮 id 的惟一性,若是有 id,則中止查找 }else if (tar.className) { objArr.push('.' + tar.className.split(' ')[0]) // 考慮若是有多個 class }else { objArr.push(tar.nodeName.toLowerCase()) } tar = tar.parentNode } objArr.pop() return objArr.reverse().join(' ') } document.addEventListener('click', function (e) { //點擊li時,返回:html body #page .content.main .refer ul li  console.log(genCssSelector(e)); }) </script> </head> <body> <div id="page"> <div class="main" id="main"> <div class="reference refer"> <ul> <li></li>  <li></li> 23333 </ul>  </div> </div>  </div> </body> </html>

獲取 dom 元素

JS獲取DOM元素的方法(8種)

  • getElementById

    只獲取到一個元素,沒有找到返回null
  • getElementsByName
  • getElementsByTagName
  • getElementsByClassName
  • document.documentElement

    獲取html
  • document.body

    獲取body
  • querySelector

    獲取一個元素
  • querySelectorAll

    獲取一組元素

獲取子元素

  • childNodes
    dom.childNodes 返回一個nodeList(元素的全部子元素)
    • nodeType
      • 元素節點的nodeType屬性值爲1
      • 屬性節點的nodeType屬性值爲2
      • 文本節點的nodeType屬性值爲3
    • nodeValue屬性

      得到和改變文本節點的值
  • firstChild 第一個子元素
  • lastChild

獲取父、兄

  • parentNode
  • nextSibling
  • previousSbiling

建立元素

  • createDocumentFragment

    建立一個dom片斷
  • createElement

    建立一個具體的元素
  • createTextNode

    建立一個文本節點

增刪改元素

  • appendChild
  • removeChild
  • replaceChild
  • insertBefore

如何用 JS 實現 JSON.parse

---eval

直接調用eval

var json = '{"a":"1", "b":2}'; var obj = eval("(" + json + ")"); // obj 就是 json 反序列化以後獲得的對象

原理

JSON 脫胎於 JS,同時也是 JS 的子集,因此可以直接交給 eval 運行

  • 加上圓括號的目的是迫使eval函數在處理JavaScript代碼的時候強制將括號內的表達式(expression)轉化爲對象,而不是做爲語句(statement)來執行

    例如對象字面量{},如若不加外層的括號,那麼eval會將大括號識別爲JavaScript代碼塊的開始和結束標記,那麼{}將會被認爲是執行了一句空語句

缺點

  • XSS 漏洞

    如:參數 json 並不是真正的 JSON 數據,而是可執行的 JS 代碼
  • 對參數 json 作校驗,只有真正符合 JSON 格式,才能調用 eval

    // 1. 用 4 個正則表達式分爲兩個階段解決(包容ie 和safari 的regexp 引擎) // 2. 將 json 反斜槓替換爲 '@' (non-json字符) // 3. 用 ']' 替換全部簡單標記 // 4. 刪除全部跟隨冒號,逗號或文本開始的方括號 // 5. 若是隻剩下 '] , { }' 則是安全的 var rx_one = /^[\],:{}\s]*$/; var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; var rx_four = /(?:^|:|,)(?:\s*\[)+/g; if ( rx_one.test( json .replace(rx_two, "@") .replace(rx_three, "]") .replace(rx_four, "") ) ) { var obj = eval("(" +json + ")"); }
---遞歸

第一種 eval 的方法,至關於一古腦兒把 JSON 字符串塞進去。
其實咱們還能夠手動逐個字符地掃描,而後進行判斷,這就是第二種方法:遞歸

// 所謂遞歸,就是重複調用value 函數 value = function () { // Parse a JSON value. It could be an object, an array, a string, a number, // or a word. white(); // 根據當前字符是什麼,咱們便能推導出後面應該接的是什麼類型 switch (ch) { case "{": return object(); case "[": return array(); case "\"": return string(); case "-": return number(); default: return (ch >= "0" && ch <= "9") ? number() : word(); } }; // 調用核心的 next 函數,逐個讀取字符 var next = function (c) { // If a c parameter is provided, verify that it matches the current character. if (c && c !== ch) { error("Expected '" + c + "' instead of '" + ch + "'"); } // Get the next character. When there are no more characters, // return the empty string. ch = text.charAt(at); at += 1; return ch; };
  • 對於常量token false true null 進行匹配,不匹配返回錯誤

以 {"a":"1", "b":2} 爲例

程序大體邏輯是:啓動 → 首次調用 value() → 發現是 { → 原來是對象,走 object() → 經過 string() 獲得 key 值爲 "a" → 讀取到冒號,哦,後面多是對象、數組、布爾值等等,具體是什麼,還得再次調用 value() 才知道 → ……

xml 解析

  • close tag

    使用一個 nodeStack 棧,在 opentag 時推入節點,close tag 時檢查當前節點是否和棧尾節點是否匹配,匹配則推出末尾節點
  • comment

參考:JSON.parse 三種實現方式

react setState

  • setState 不保證同步

    • 可能會爲了性能收益批量執行
    • setState() 不會馬上改變 this.state,而是建立一個即將處理的 state 轉變。在調用該方法以後訪問 this.state可能會返回現有的值。
    解決方案
    • 使用回調函數

      setState 方法接收一個 function 做爲回調函數。這個回掉函數會在 setState 完成之後直接調用,這樣就能夠獲取最新的 state

      this.setState({ selection: value }, this.fireOnSelect)
    • setTimeout
      在 setState 使用 setTimeout 來讓 setState 先完成之後再執行裏面內容

      this.setState({ selection: value }); setTimeout(this.fireOnSelect, 0);
  • 形成沒必要要的渲染
    • shouldComponentUpdate 解決

      setState() 將老是觸發一次重繪,除非在 shouldComponentUpdate() 中實現了條件渲染邏輯
    • 和渲染無關的狀態儘可能不要放在 state 中來管理

      一般 state 中只來管理和渲染有關的狀態 ,從而保證 setState 改變的狀態都是和渲染有關的狀態。這樣子就能夠避免沒必要要的重複渲染。其餘和渲染無關的狀態,能夠直接以屬性的形式保存在組件中,在須要的時候調用和改變,不會形成渲染。

  • 不能頗有效的管理全部的組件狀態

參考:淺談使用React.setState須要注意的三點

3. 百度

一面

jsonp cors

css

  • 分欄佈局哪些方式
  • 詳細說下flex

js & jq

  • 如何獲取一個元素(js jq)
  • 異步事件是如何發送的,經常使用機制
  • 有哪些接收後臺數據的方法
    • ajax
    • fetch
    • jsonp
    • websocket
    • SSE
  • event loop
  • 喜歡 es6 的哪些屬性
  • 箭頭函數與普通函數的不一樣
  • 閉包
    • 簡述
    • 應用
    • 在循環中如何用其餘方式代替閉包

vue

  • 和 react 區別
  • 如何向服務器傳遞數據

操做系統

  • 線程 && 進程

計算機網絡

  • 除了tcp 還用到哪些
  • http 與 tcp 分別屬於第幾層

數據結構

  • 有哪些線性存儲空間(array 棧)

二面

  • 如何作一個 css 選擇器

    見本文 阿里-在線編程
  • 給定一組dom 節點,和一個css樣式表,找出不含有樣式的dom

    面試官很耐心的解釋了,仍是沒聽明白題目
  • 鏈表操做
    • 單向鏈表反轉
  • 居中問題

    沒有詳細問,我分了兩個方面分別回答
    • 水平居中
    • 垂直水平
    詳細見 CSS 居中
  • get & post

    回答了大多數應聘者的 「標準答案」, 但經面試官指點,頓悟,大概這就叫高手吧

三面

  • 數字的存儲

  • 前端的編碼問題
    • 種類
    • 亂碼如何處理
  • 有哪些協議,分別有什麼做用
  • 關於實習經歷,找實習的過程,項目中的二三事及解決方案
  • 大學中最有成就感的事
  • 閒聊了一下,關於保研等等,總共 四十分鐘

分欄佈局

---等分佈局

 

 

  • float
    • 原理:增大父框的實際寬度後,使用CSS3屬性box-sizing進行佈局的輔助。

    • 用法:先將父框設置爲 margin-left: -*px,再設置子框 float: left、width: 25%、padding-left、box-sizing: border-box

      .parent{ margin-left: -20px; } .column{ float: left; width: 25%; padding-left: 20px; box-sizing: border-box; /*包含padding區域 w+g*/ }
  • table
    • 原理:經過增長一個父框的修正框,增大其寬度,並將父框轉換爲 table,將子框轉換爲 tabel-cell 進行佈局。

    • 用法:先將父框的修正框設置爲 margin-left: -*px,再設置父框 display: table、width:100%、table-layout: fixed,設置子框 display: table-cell、padding-left

      .parent-fix{ margin-left: -20px; } .parent{ display: table; width:100%; table-layout: fixed; } .column{ display: table-cell; padding-left: 20px; }
  • flex
    • 原理:經過設置CSS3佈局利器flex中的flex屬性以達到等分佈局。

    • 用法:將父框設置爲display: flex,再設置子框flex: 1,最後設置子框與子框的間距margin-left。

如何獲取一個class

// 直接獲取---須要高版本瀏覽器支持 document.querySelectorAll("div.aa") // 相似屬性選擇器的寫法 document.querySelectorAll("div[class='aa']") // 補充一下還能夠not選擇器 document.querySelectorAll(".aa:not(ul)") document.getElementsByClassName('cls') // jq $('.className')

Event Loop

js 單線程:
用途決定,操做 DOM

任務隊列

排隊緣由:計算量大的同步執行,IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據)異步。

異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行

"任務隊列"中的事件,除了IO設備的事件之外,還包括一些用戶產生的事件(好比鼠標點擊、頁面滾動等等)。只要指定過回調函數,這些事件發生時就會進入"任務隊列",等待主線程讀取。

  • 全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。

  • 主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。

  • 一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。

  • 主線程不斷重複上面的第三步。

只要主線程空了,就去檢查異步的任務隊列,若是異步事件觸發,則將其加到主線程的執行棧

深刻了解定時器

  • 零延遲 setTimeout(func, 0)

    零延遲並非意味着回調函數馬上執行。它取決於主線程當前是否空閒與「任務隊列」裏其前面正在等待的任務。
  • 調用setTimeout()以後,該方法會返回一直數值ID,表示超時調用。這個超時調用ID是計劃執行代碼的惟一標識符,能夠經過它來取消超時調用

  • 超時調用的代碼都是在全局做用域中執行的,所以函數中this的值在非嚴格模式下指向window對象,嚴格模式下是undefined。

Event Loop

異步與event loop沒有太直接的關係,準確的來說event loop 只是實現異步的一種機制
主任務 ——> micro task ——> 渲染視圖 ——> macro task

主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)

Javascript 中的事件循環是以任務爲單位的,將不少個待執行的任務串聯在一塊兒就造成了隊列 Task Queue,不少的隊列前後按順序執行任務就造成了 Event Loop

一個事件循環(EventLoop)中會有一個正在執行的任務(Task),而這個任務就是從 macrotask 隊列中來的。
當這個 macrotask 執行結束後,全部可用的 microtask 將會在同一個事件循環中執行
當這些 microtask 執行結束後還能繼續添加 microtask 一直到真個 microtask 隊列執行結束。

  • 一個事件循環(event loop)會有一個或多個任務隊列(task queue)

    task queue 就是 macrotask queue
  • 每個 event loop 都有一個 microtask queue
  • task queue == macrotask queue != microtask queue
  • 一個任務 task 能夠放入 macrotask queue 也能夠放入 microtask queue 中
  • 當一個 task 被放入隊列 queue(macro或micro) 那這個 task 就能夠被當即執行了

Micro Task

當咱們想以同步的方式來處理異步任務時候就用 microtask(好比咱們須要直接在某段代碼後就去執行某個任務,就像Promise同樣)

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

Macro Task

  • setTimeout
  • setInterval
  • setImmediate
  • I/O

任務隊列中,在每一次事件循環中,從 macrotask 隊列開始執行,macrotask只會提取一個執行,而microtask會一直提取,直到microsoft隊列爲空爲止。

也就是說若是某個microtask任務被推入到執行中,那麼當主線程任務執行完成後,會循環調用該隊列任務中的下一個任務來執行,直到該任務隊列到最後一個任務爲止。而事件循環每次只會入棧一個macrotask,主線程執行完成該任務後又會檢查microtasks 隊列並完成裏面的全部任務後再執行macrotask的任務。

執行過程以下:

  • 主線程空閒時首先執行 micro
  • 以後從 macro 中提取一個 task 到主任務,完成後再次執行 micro queue(執行一個cycle)
  • 反覆過程2, 每一個週期爲一個事件循環

爲啥要用 microtask?

  • micro 執行總在 macro 以前
  • micro 所有執行完畢後會更新 UI 和執行下一個macro
    根據HTML Standard,在每一個 task 運行完之後,UI 都會重渲染,那麼在 microtask 中就完成數據更新,當前 task 結束就能夠獲得最新的 UI 了
// 驗證 (function () { const $test = document.getElementById('test') let counter = 0 function func1() { $test.innerText = ++counter alert('func1') } function func2() { $test.innerText = ++counter alert('func2') } function func3() { $test.innerText = ++counter alert('func3') } function func4() { $test.innerText = ++counter alert('func4') } (function () { // main task func1() // macro task setTimeout(() => { func2() // micro task Promise.resolve().then(func4) }, 0); // macro task setTimeout(func1, 0); // micro task Promise.resolve().then(func3) // main task func4() })() // alert func1 // alert func4 // alert func3 // UI update ---> counter = 3 // alert func2 // alert func4 // UI update ---> counter = 5 // alert func1 // UI update ---> counter = 6 })()

接收後臺資源的方法(除了Ajax)

  • Ajax
  • fetch
    返回一個Promise對象, 根據 Promise Api 的特性, fetch能夠方便地使用then方法將各個處理邏輯串起來

    mode

    // fetch能夠設置不一樣的模式使得請求有效 fetch(url, {mode: 'cors'});
  • Jsonp

  • websocket
    服務器推送技術之一
    全雙工
  • SSE(server-sent-events)
    單向通道(服務器 -> 瀏覽器)

異步編程經常使用方法

ES 6之前:

  • 回調函數
  • 事件監聽(事件發佈/訂閱)
  • Promise對象

ES 6:

  • Generator函數(協程coroutine)

ES 7:

  • async和await

回調函數

通常是須要在一個耗時操做以後執行某個操做時能夠使用回調函數

  • 定時器
  • 讀取文件

問題:
在回調函數以外沒法捕獲到回調函數中的異常

var fs = require('fs'); try{ fs.readFile('not_exist_file', 'utf8', function(err, data){ console.log(data); }); } catch(e){ console.log("error caught: " + e); }

嘗試讀取一個不存在的文件,這固然會引起異常,可是最外層的try/catch語句卻沒法捕獲這個異常。這是異步代碼的執行機制致使的

爲何異步代碼回調函數中的異常沒法被最外層的try/catch語句捕獲?

異步調用通常分爲兩個階段,提交請求和處理結果,這兩個階段之間有事件循環的調用,它們屬於兩個不一樣的事件循環(tick),彼此沒有關聯。

異步調用通常以傳入callback的方式來指定異步操做完成後要執行的動做。而異步調用本體和callback屬於不一樣的事件循環。

try/catch語句只能捕獲當次事件循環的異常,對callback無能爲力。

事件監聽(訂閱-發佈)

典型的邏輯分離方式,對代碼解耦頗有用處
把不變的部分封裝在組件內部,供外部調用,須要自定義的部分暴露在外部處理。
從某種意義上說,事件的設計就是組件的接口設計。

//發佈和訂閱事件 var events = require('events'); var emitter = new events.EventEmitter(); emitter.on('event1', function(message){ console.log(message); }); emitter.emit('event1', "message for you");

Promise 對象

用同步操做的流程寫法來表達異步操做,避免了層層嵌套的異步回調

  • Promise.prototype.then()

    //原生Primose順序嵌套回調示例 var fs = require('fs') var read = function (filename){ var promise = new Promise(function(resolve, reject){ fs.readFile(filename, 'utf8', function(err, data){ if (err){ reject(err); } resolve(data); }) }); return promise; } read('./text1.txt') .then(function(data){ console.log(data); return read('./text2.txt'); // 返回了一個新的Promise實例 }) .then(function(data){ console.log(data); });
    Promise構造函數的參數是一個函數,在這個函數中咱們寫異步操做的代碼
    在異步操做的回調中,根據err變量來選擇是執行resolve方法仍是reject方法
    • 通常來講調用resolve方法的參數是異步操做獲取到的數據(若是有的話),但還多是另外一個Promise對象,表示異步操做的結果有多是一個值
    • 也有多是另外一個異步操做,調用reject方法的參數是異步回調用的err參數

調用read函數時,實際上返回的是一個Promise對象,經過在這個Promise對象上調用then方法並傳入resolve方法和reject方法來指定異步操做成功和失敗後的操做。

  • Promise.prototype.catch()

    用於指定發生錯誤時的回調函數

    read('./text1.txt') .then(function(data){ console.log(data); return read('not_exist_file'); }) .then(function(data){ console.log(data); }) .catch(function(err){ console.log("error caught: " + err); }) .then(function(data){ console.log("completed"); })

    使用Promise對象的catch方法能夠捕獲異步調用鏈中callback的異常
    Promise對象的catch方法返回的也是一個Promise對象,所以,在catch方法後還能夠繼續寫異步調用方法

  • Promise異步併發
    • Promise.all()
      • 將多個Promise實例,包裝成一個新的Promise實例
        var p = Promise.all([p1,p2,p3]);
      • 接受一個數組做爲參數,p一、p二、p3都是Promise對象實例
      • 只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。
      • 只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

        var promises = [1, 2].map(function(fileno){ return read('./text' + fileno + '.txt'); }); Promise.all(promises) .then(function(contents){ console.log(contents); }) .catch(function(err){ console.log("error caught: " + err); })
    • Promise.race()
      • 將多個Promise實例,包裝成一個新的Promise實例
        var p = Promise.race([p1,p2,p3]);
      • p一、p二、p3只要有一個實例率先改變狀態,p的狀態就會跟着改變,那個率先改變的Promise實例的返回值,就傳遞給p的返回值。
      • 若是Promise.all方法和Promise.race方法的參數不是Promise實例,就會先調用下面講到的Promise.resolve方法,將參數轉爲Promise實例,再進一步處理
    • Promise.resolve()
      • 將現有對象轉換成Promise對象
      var p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });
    • Promise.reject()
      • 返回一個新的Promise實例,該實例的狀態爲rejected。
      • Promise.reject 方法的參數reason,會被傳遞給實例的回調函數。
      var p = Promise.reject('出錯了'); p.then(null, function (s){ console.log(s) });
  • Generator函數
    • 能夠交出函數的執行權(暫停執行)
    • 整個Generator函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操做須要暫停的地方,都用yield語句註明。

      function* gen(x){ var y = yield x + 2; return y; } var g = gen(1); var r1 = g.next(); // { value: 3, done: false } console.log(r1); var r2 = g.next() // { value: undefined, done: true } console.log(r2);

    **Generator函數的函數名前面有一個"*"**

    • 調用Generator函數,會返回一個內部指針(即遍歷器)g,這是Generator函數和通常函數不一樣的地方,調用它不會返回結果,而是一個指針對象。
    • 調用指針g的next方法,會移動內部指針,指向第一個遇到的yield語句
      • next方法的做用是分階段執行Generator函數。每次調用next方法,會返回一個對象,表示當前階段的信息(value屬性和done屬性)
      • value屬性是yield語句後面表達式的值,表示當前階段的值;
      • done屬性是一個布爾值,表示Generator函數是否執行完畢,便是否還有下一個階段
  • Thunk 函數
    Thunk 函數的含義和用法
  • ES 7中的async和await

  • fetch
    • 替代瀏覽器原生的XMLHttpRequest異步請求

來自深刻解析Javascript異步編程

get & post 剖析

入門回答

區別:

  • get
    • 參數包含在 URL 中
    • 大小有限制
    • 使用Request.QueryString 獲取變量的值
    • 安全性差,直觀顯示
  • post
    • 經過 request body 傳遞參數
    • 經過 Request.Form 獲取變量的值

當我滿心充滿着自信和喜悅時,彷彿看到了面試官眉頭一皺,So?

「標準答案」
aspect GET POST
瀏覽器回退 無影響 回退會再次提交請求
地址標記 產生的 URL 地址能夠被 Bookmark 提交地址不被標記
cache 該請求會被瀏覽器主動 cache 該請求不會被緩存
編碼 只能進行url編碼 支持多種編碼方式
參數保留 請求參數會被完整保留在瀏覽器歷史記錄裏 POST中的參數不會被保留
長度限制 有(瀏覽器限制,IE-2083個字符) 無(限制做用的是服務器的處理程序的處理能力)
參數類型 只接受ASCII字符 沒有限制
參數傳遞 經過URL傳遞 放在Request body中
裸奔剖析

99%的人都理解錯了HTTP中GET與POST的區別

前端編碼

字符 表示 補充
二進制 0/1 八個二進制位能夠組合出256種狀態,這被稱爲一個字節(byte)
八進制 0~7  
十進制 0~9  
十六禁止 0~9 A~F  
  • 8byte = 1bit
  • 1024 字節 = 1k
  • 1024k = 1M
  • 1024M = 1G
  • 1024G = 1T
編碼 特徵 補充
二十一進制碼(BCD碼) 保留了十進制數的權,而數字則用二進制數碼0和1的組合來表示 在須要高精度的計算中BCD編碼比較經常使用(瞭解)
ASCII碼 美國信息交換標準委員會制定的7位字符編碼,用7位二進制碼錶示一個字符,第8 位用於肯定附加的128 個特殊符號字符、外來語字母和圖形符號  
GB2312 爲了保存非英文,使用127號以後的空位保存新的字母(一個8位的字節能夠組合256種狀態,ASCII只編到127號),一直編到最後一位255,並且不一樣國家表示的符號也不同,也能夠說GB2312是對ASCII的中文擴展 不夠用,後來只要求只要第一個字節是大於127就固定表示這是一個漢字的開始,稱之爲GBK編碼
GB18030 / DBCS 編碼中又增長了幾千個新的少數民族的字,GBK擴展成了GB18030統稱它們叫作DBCS  
Unicode ISO(國際標準化組織)廢棄了全部地區性編碼方案,作了一套包括了地球上全部文化、符號以及字母的編碼;ISO規定:必須用兩個字節,16位來統一表示全部的字符,不管是半角的英文字母,仍是全角的漢字,它們都是統一的一個字符!也就是兩個字節  
UTF-8 UTF-8 互聯網上使用最廣的一種 Unicode 的實現方式,每次以8個位爲單位傳輸數據;UTF-16就是每次 16 個位 UTF-8 最大的一個特色,就是它是一種變長的編碼方式,Unicode一箇中文字符佔 2 個字節,而UTF-8一箇中文字符佔3個字節,UTF-8是Unicode的實現方式之一

進制轉換

------十進制轉其餘------- var a = 24; a.toString(2);//11000 a.toString(8);//30 a.toString(16);//18 ------其餘轉十進制------- var b=11000,c=30,d=18; console.log(parseInt(b, 2)); // 二進制轉十進制 console.log(parseInt(c, 8)); // 八進制轉十進制 console.log(parseInt(d, 16));// 十六進制轉十進制

前端編碼問題

在使用nodeJS編寫前端工具時,對文本文件的操做比較多,這就涉及到了文件的編碼問題,經常使用的文本編碼有UTF8和GBK兩種,而且UTF8文件還可能帶有BOM(字節順序標記),在讀取不一樣編碼的文本文件時,須要將文件內容轉換爲JS使用的UTF8編碼字符串後才能正常處理

  • 移除 BOM

    BOM用於標記一個文本文件使用Unicode編碼,其自己是一個Unicode字符("\uFEFF"),位於文本文件頭部以告訴其餘編輯器以utf8來顯示字符

    可是在網頁上並不須要添加BOM頭識別,由於網頁上能夠使用 head頭 指定charset=utf8告訴瀏覽器用utf8來解釋.可是你用window自動的編輯器,編輯,而後有顯示在網頁上這樣就會顯示出0xEF 0xBB 0xBF這3個字符。這樣網頁上就須要去除0xEF 0xBB 0xBF
    • 能夠使用editplus 選擇不帶BOM的編碼,這樣就能夠去除了
    • js 去除

      // 能夠經過文件頭的幾個字節來判斷文件是否包含BOM以及使用哪一種Unicode,若是讀取文件的時候不去掉BOM // 假如咱們將幾個JS文件合併成一個,若是文件中含有BOM字符,就會致使語法錯誤 // 因此咱們用 nodeJS讀取文件是通常先去掉BOM var bin = fs.readFileSync(pathname);//經過node 中fs模塊同步讀文件內容 //判斷文件頭的字節 if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) { bin = bin.slice(3); }
  • GBK 轉 UTF8

    NodeJS支持在讀取文本文件時,或者在Buffer轉換爲字符串時指定文本編碼,但GBK編碼不在NodeJS自身支持範圍內,通常咱們藉助iconv-lite這個三方包來轉換編碼,首先使用npm下載這個第三方包,讀取GBK文件函數以下:

    var iconv = require('iconv-lite'); function readGBKText(pathname) { var myFs = fs.readFileSync(pathname); return iconv.decode(myFs, 'gbk'); }

數據結構

經常使用的線性結構有:線性表,棧,隊列,循環隊列,數組
線性表中包括順序表、鏈表等,其中:

  • 棧和隊列只是屬於邏輯上的概念,實際中不存在,僅僅是一種思想,一種理念;
  • 線性表則是在內存中數據的一種組織、存儲的方式。

常見的排序算法及其時間複雜度

參考:十大經典排序算法總結(JavaScript描述)

其餘

  • git 與 GitHub 有什麼區別
  • git 的一些指令
  • Linux

計算機網絡

**若有不足,歡迎交流,祝各位看官 offer 拿到手軟 O(∩_∩)O**

 

看樣子博主成功了哈。 恭喜。。。 我面了兩次阿里都掛在電話面試了,第一次一年前推了個p6的,問了不少react的問題,掛了,第二次前幾天, p7的(推簡歷的給我推了個p7,我崩潰) 其餘的答的都還好主要問到react內部實現,redux connect方法的實現等

 

 

 

 

前端相關片斷整理——持續更新

 

 

更好閱讀 移步這裏

1. ES6

  • 箭頭函數
  • 字符串模板
  • generators(生成器)
  • async/await
  • 解構賦值
  • class
  • 引入module模塊的概念

1.1. 箭頭函數:

  • 函數內的this對象,是定義時所在的對象,不是使用時所在的對象
  • 不可當構造函數
  • 用rest代替argument
  • this指向通常可變,但在箭頭函數中固定
  • 簡單,單行,不會複用的函數建議使箭頭函數
    複雜,行多,使用傳統

1.2. promise

解決異步回調多層嵌套的問題

  • 是一個容器;
    包含某個將來結束的事件
  • 是一個對象:
    從它可獲取異步操做的消息
  1. pending 進行中
  2. resolved 已完成
  3. rejected 已失敗

特色

  • 狀態不受外界影響,只有事件結果決定
  • 狀態改變不會再變

缺點:

  • 沒法取消promise,一旦創建當即執行,中途沒法撤回
  • 無回掉函數的話,錯誤不反應到外部
  • pending時,狀態沒法得知

Promise.all

接收 Promise 數組爲參數,將多個Promise實例,包裝成一個新的Promise實例,全部 resolve ,返回全部值

在不一樣的接口請求數據而後拼合成本身所需的數據,一般這些接口之間沒有關聯(例如不須要前一個接口的數據做爲後一個接口的參數)

var p = Promise.all([p1, p2, p3]);

p的狀態由p一、p二、p3決定,分紅兩種狀況:

  • 只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。
  • 只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

Promise.race
它一樣接收一個數組,不一樣的是隻要該數組中的 Promise 對象的狀態發生變化(不管是 resolve 仍是 reject)該方法都會返回

async/await

async 會將其後的函數(函數表達式或 Lambda)的返回值封裝成一個 Promise 對象,而 await 會等待這個 Promise 完成,並將其 resolve 的結果返回出來

  • 是寫異步代碼的新方式,之前的方法有回調函數和Promise。
  • 是基於Promise實現的,它不能用於普通的回調函數。
  • 與Promise同樣,是非阻塞的。
  • 使得異步代碼看起來像同步代碼,這正是它的魔力所在。

1.3. interator

是一種接口,爲全部數據結構提供一種統一的訪問機制,即for...of 循環

做用:

  • 一是爲各類數據結構,提供一個統一的、簡便的訪問接口;
  • 二是使得數據結構的成員可以按某種次序排列;
  • 三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。

interator遍歷過程:

  1. 建立一個只針對象,指向當前數據結構的起始位置(遍歷器對象本質是指針對象)
  2. 調用指針對象的next方法

使用場合:

  • 解構賦值
  • 擴展運算符(...)
  • yield*

for...in

  • 爲遍歷對象設計,不適用數組
  • key
  • 以字符串做爲鍵名
  • 遍歷數字鍵以及手動添加的其餘鍵
  • 可能會以任意順序遍歷鍵名

for...of

  • 語法簡潔,無以上缺點
  • 循環value
  • 不一樣用於foreach方法,能夠與break,continue,return配合使用
  • 提供了遍歷全部數據結構的統一操做接口,循環普通對象結合 bject.keys() 搭配使用
  • 可自動遍歷generator函數生成的iterator對象
  • 除了遍歷數組元素之外,還會遍歷自定義屬性

1.4. generator 函數

一種異步解決方案(一種封裝了多個內部狀態的狀態機)

  • 返回的不是函數運行結果,而是指向內部狀態的指針對象
  • 調用next方法,從中止地方開始執行,移向下一個狀態

1.5. yield 與 return

類似:都能返回緊跟在語句後面那個表達式的值
區別:記憶功能,執行次數,返回值數量

1.6. 回調函數

JavaScript對異步編程的實現

1.7. ES6 Object.assign

將源對象(source)的全部可枚舉屬性,複製到目標對象(target)

Object.assign(target, source1, source2);
後面屬性覆蓋前面同名屬性

  • 一個參數時,返回該參數
  • 參數不是對象,轉成對象(undefined,null會報錯),若爲源對象位置,則跳過
  • 可用來操做數組,將數組視爲對象
  • 淺拷貝非深拷貝(若源對象的有對象屬性值,則拷貝的是該引用)

用途:

  • 爲兌現添加屬性/方法
  • 克隆對象
  • 合併對象
  • 爲屬性指定默認值

2. 通訊

2.1. JSONP

被包含在一個回調函數中的 json
核心是: 動態添加script標籤調用服務器提供的js腳本

2.2. cors

使用自定義的http頭部讓瀏覽器與服務器進行溝通,肯定該請求是否成功
核心:由服務器發送一個響應標頭

2.3. web安全

1) 將重要的cookie標記爲http only
2) 只容許用戶輸入指望值
3) encode
4) 過濾或移除特殊標籤
5) 過濾JavaScript事件標籤

3. 架構

3.1. 模塊化

原理: 將複雜系統分解爲代碼結構更合理,可維護性更高,可管理的模塊
目的: 只需完成本身的業務代碼
發展過程:

  1. commonjs
    模塊爲單獨的文件,require,同步使用
    nodejs主要用於服務器進程,加載內容在本地磁盤
    異步狀況:瀏覽器環境中須要從服務器加載模塊,須要採用異步模式
  2. AMD(Asynchronous Module Definition)
    • 容許輸出模塊兼容commonjs規範
    • require([module], callback);
    • 模塊加載與調用不一樣步,瀏覽器不會發生假死
    • requirejs curljs
  3. CMD
    seajs推廣中對模塊定義的產出

CMD與AMD區別:

  • amd推崇依賴前置(定義模塊時申明其依賴的模塊),cmd推崇依賴就近(用時再require)
  • amd的api默認一當多,cmd推崇職責單一(amd中require分全局和局部)

requirejs 與 seajs 分析:

  1. 定位,requirejs想成爲瀏覽器端的模塊加載器,也想成爲rhino/node等環境的模塊加載器
    seajs專一web瀏覽器端,經過node擴展方式方便跑在node端
  2. 標準,requirejs醉醺amd規範,seajs遵循cmd,api不一樣
  3. 理念,requirejs嘗試讓第三方類庫修改自身來支持requirejs,seajs不強推,採用資助封裝方式,已較成熟封裝策略
  4. 質量,require<seajs
  5. 插件

更多瞭解:

3.2. react

用於構建用戶界面的JavaScript庫,主要用於構建ui,將普通的DOM以數據結構的形式展示出來

永遠只須要關心數據總體,兩次數據之間的UI如何變化,則徹底交給框架去作,使用React大大下降了邏輯複雜性
Virtual DOM並無徹底實現DOM,Virtual DOM最主要的仍是保留了Element之間的層次關係和一些基本屬性

基於React進行開發時全部的DOM構造都是經過虛擬DOM進行,每當數據變化時,React都會從新構建整個DOM樹,而後React將當前整個DOM樹和上一次的DOM樹進行對比,獲得DOM結構的區別,而後僅僅將須要變化的部分進行實際的瀏覽器DOM更新

虛擬DOM是內存數據,性能是極高的,而對實際DOM進行操做的僅僅是Diff部分,於是能達到提升性能的目的。這樣,再也不須要關注某個數據的變化如何更新到一個或多個具體的DOM元素,而只須要關心在任意一個數據狀態下,整個界面是如何Render的

設計特色:

  • 變換:react核心認爲ui只是把數據經過映射關係變換成另外一種形式的數據——函數
  • 組合:將兩個或多個不一樣的抽象合併爲一個
  • 組件化:推薦以組件的方式思考ui構成,將小組件經過組合或嵌套構成大組件

組件特徵:

  • 可組合
  • 可重用
  • 可維護

jsx語法:
HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它容許 HTML 與 JavaScript 的混寫

生命週期:
組件的生命週期分紅三個狀態:

  • Mounting:已插入真實 DOM
  • Updating:正在被從新渲染
  • Unmounting:已移出真實 DOM

React 爲每一個狀態都提供了兩種處理函數,will 函數在進入狀態以前調用,did 函數在進入狀態以後調用,三種狀態共計五種處理函數:

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

兩種特殊狀態的處理函數:

  • componentWillReceiveProps(object nextProps):已加載組件收到新的參數時調用
  • shouldComponentUpdate(object nextProps, object nextState):判斷是否從新渲染時調用

3.3. angular

特性:

  • MVVM
  • 模塊化
  • 自動化雙向數據綁定
  • 語義化標籤
  • 依賴注入

3.4. vue

  • 父-子
    props
  • 子-父
    on/emit
  • 其餘
    使用空的vue實例做爲中央事件總線

3.5. angular與react之對比

  • React 和 Angular 之間的巨大差別是 單向與雙向綁定
  • React 和 Vue 都使用了虛擬 DOM —— 沒必要在每一個元素每次變化時從新渲染整個巨大的table
  • 若是應用時常要處理大量的動態數據集,並以相對簡便和高性能的方式對大型數據表進行顯示和變動,因爲雙向數據綁定須要監聽每個可變元素, 數據量變大就會帶來顯著的性能問題,React是至關不錯的選擇。可是React不像AngularJS那樣包含完整的功能,舉例來講,React沒有負責數據展示的控制器

3.6. 軟件架構

模式之間不一樣 主要是 M與V 的數據傳遞的流程不一樣

3.6.1. mvc

  • View 傳送指令到 Controller
  • Controller 完成業務邏輯後,要求 Model 改變狀態
  • Model 將新的數據發送到 View,用戶獲得反饋

MVC 能夠分紅兩種方式:

  • 經過 View 接受指令,傳遞給 Controller
  • 直接經過controller接受指令

3.6.2. MVP

  • 各部分之間的通訊,都是雙向的。
  • View 與 Model 不發生聯繫,都經過 Presenter 傳遞。
  • View 很是薄,不部署任何業務邏輯,稱爲"被動視圖"(Passive View),即沒有任何主動性,而 Presenter很是厚,全部邏輯都部署在那裏。

3.6.3. MVVM

  • 用數據「綁定」的形式讓數據更新的事件不須要開發人員手動去編寫特殊用例,而是自動地雙向同步。
  • 數據綁定能夠認爲是Observer模式或者是Publish/Subscribe模式,原理都是爲了用一種統一的集中的方式實現頻繁須要被實現的數據更新問題。
  • MVVM不只簡化了業務與界面的依賴關係,還優化了數據頻繁更新的解決方案

3.7. restful架構

Fielding將他對互聯網軟件的架構原則,定名爲REST,即Representational State Transfer的縮寫。我對這個詞組的翻譯是"資源的表現層狀態轉化"。

4. js

4.1. js垃圾回收與內存管理

各大瀏覽器一般用採用的垃圾回收有兩種方法:標記清除、引用計數

4.1.1. 垃圾回收

自動垃圾回收機制(GC:Garbage Collecation),也就是說,執行環境會負責管理代碼執行過程當中使用的內存
垃圾收集器會按期(週期性)找出那些不在繼續使用的變量,而後釋放其內存

  • 標記清除
    1. 垃圾收集器在運行的時候會給存儲在內存中的全部變量都加上標記
    2. 而後,它會去掉環境中的變量以及被環境中的變量引用的標記
    3. 而在此以後再被加上標記的變量將被視爲準備刪除的變量,緣由是環境中的變量已經沒法訪問到這些變量了
    4. 最後,垃圾收集器完成內存清除工做,銷燬那些帶標記的值,並回收他們所佔用的內存空間
  • 引用計數
    跟蹤記錄每一個值被引用的次數
    當聲明瞭一個變量並將一個引用類型賦值給該變量時,則這個值的引用次數就是1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數就減1,釋放那些引用次數爲0的值所佔的內存。

    function problem() { var objA = new Object(); var objB = new Object(); objA.someOtherObject = objB; objB.anotherObject = objA; }

    這個方式存在一個比較大的問題就是循環引用
    能夠手動切斷他們的循環引用

    myObj.element = null; element.someObject =null;

4.1.2. 減小JavaScript中的垃圾回收

  • 在初始化的時候新建對象,而後在後續過程當中儘可能多的重用這些建立好的對象。
  • 另外還有如下三種內存分配表達式(可能不像new關鍵字那麼明顯了):
    • {} (建立一個新對象)
    • [] (建立一個新數組)
    • function() {…} (建立一個新的方法,注意:新建方法也會致使垃圾收集!!)

4.1.3. 優化

  1. 對象object優化
    • 避免使用new/{}來新建對象
    • cr.wipe(obj)—遍歷此對象的全部屬性,並逐個刪除,最終將對象清理爲一個空對象
  2. 數組array優化

    arr = []; //將原數組變成一小片內存垃圾 arr.length = 0 //清空數組

4.2. 閉包

特色:

  • 函數
  • 能訪問另一個函數做用域中的變量

ES 6以前,Javascript只有函數做用域的概念,沒有塊級做用域。即外部是訪問不到函數做用域中的變量。

總結

  • 能夠訪問外部函數做用域中變量的函數
  • 被內部函數訪問的外部函數的變量能夠保存在外部函數做用域內而不被回收---這是核心,後面咱們遇到閉包都要想到,咱們要重點關注被閉包引用的這個變量

4.3. 做用域鏈

爲何閉包就能訪問外部函數的變量呢

Javascript中有一個執行環境(execution context)的概念,它定義了變量或函數有權訪問的其它數據,決定了他們各自的行爲。每一個執行環境都有一個與之關聯的變量對象,環境中定義的全部變量和函數都保存在這個對象中

當訪問一個變量時,解釋器會首先在當前做用域查找標示符,若是沒有找到,就去父做用域找,直到找到該變量的標示符或者再也不存在父做用域了,這就是做用域鏈。

做用域鏈的頂端是全局對象。對於全局環境中的代碼,做用域鏈只包含一個元素:全局對象

做用域鏈和原型繼承:
有點相似,但又有點小區別:

  • 若是去查找一個普通對象的屬性時,在當前對象和其原型中都找不到時,會返回undefined
  • 查找的屬性在做用域鏈中不存在的話就會拋出ReferenceError

更多瞭解:

閉包的運用

  • 匿名自執行函數
    有的函數只須要執行一次,其內部變量無需維護,執行後釋放變量
  • 實現封裝/模塊化代碼
    變量做用域爲函數內部,外部沒法訪問
  • 實現面向對象中的對象
    這樣不一樣的對象(類的實例)擁有獨立的成員及狀態,互不干涉

優勢:

  • 可讓一個變量常駐內存 (若是用的多了就成了缺點
  • 避免全局變量的污染
  • 私有化變量

缺點:

  • 由於閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存
  • 引發內存泄露

4.4. 事件委託和this

4.4.1. 事件委託

由其它元素而非事件目標元素來響應事件產生的行爲的思想。如用ul元素來處理其子元素li的事件。

事件冒泡: stopPropagation、stopImmediatePropagation、preventDefault

訂閱發佈
優勢:減小監聽器數量,改善性能
缺點:父容器的偵聽器可能須要檢查事件來選擇正確的操做

4.4.2. this

this 關鍵字在JavaScript中的一種經常使用方法是指代碼當前上下文

  • 默認指向全局對象,其一般是window
  • this老是表明它的直接調用者(js的this是執行上下文), 例如 obj.func ,那麼func中的this就是obj
  • 在嚴格模式下,沒有直接調用者的函數中的this是 undefined
  • 使用call,apply,bind綁定的,this指的是 綁定的對象

在異步編程中,this能夠很容易改變過程當中一個功能操做。保持處理程序上下文的一個小技巧是將其設置到閉包內的一個變量,當在上下文改變的地方調用一個函數時,如setTimeout,你仍然能夠經過該變量引用須要的對象。

箭頭函數中的this

  • 箭頭函數沒有本身的this, 它的this是繼承而來
  • 默認指向在定義它時所處的對象(宿主對象),而不是執行時的對象, 定義它的時候,可能環境是window
  • 箭頭函數能夠方便地讓咱們在 setTimeout ,setInterval中方便的使用this

持續更新,歡迎交流~

 

 
 
 
 
 

前端基礎精簡總結

 

 

更好閱讀 移步這裏

1. JavaScript

1.1. 基礎語法

包括:變量聲明、數據類型、函數、控制語句、內置對象等

1.1.1. 變量聲明

ES5:

var //普通變量 function //函數

ES6新增:

let //普通變量 const //常量 -- 聲明的同時初始化,不可從新賦值,不能重複聲明 import //模塊 class //類
1.1.2. 數據類型
-- 類型介紹

ES5:
String、Number、Boolean、Null、Undefined、Object
ES6增:
Symbol
其中,object爲引用,其餘爲基本類型

  • 基本類型
    佔據空間固定,是簡單的數據段,將其存儲在(stack)中(按值訪問) 便於提高變量查詢速度
    爲了便於操做這類數據,ECMAScript提供了 3 個 基本包裝類型 :Boolean、Number 和 String
    • 基本包裝類型
      一種特殊的引用類型,每當讀取一個基本類型值的時候,JS內部就會建立一個對應的包裝對象,從而能夠調用一些方法來操做這些數據
  • 引用類型
    • 因爲其值的大小會改變,因此不能將其存放在棧中,不然會下降變量查詢速度
    • 將其存儲在(heap)中,存儲在變量處的值是一個指針,指向存儲對象的內存處(按址訪問)
    • 能夠爲其添加、改變和刪除屬性和方法;但基本類型不能夠添加屬性和方法
-- 類型判斷
  • 原始類型
    • typeof
  • 引用類型
    • isinstanceof -- 判斷已知對象
    • constructor -- 根據對象的constructor判斷
      constructor原本是原型對象上的屬性,指向構造函數。可是根據實例對象尋找屬性的順序,若實例對象上沒有實例屬性或方法時,就去原型鏈上尋找,所以,實例對象也是能使用constructor屬性的
    • Object.prototype.toString.call
    • $.type() -- 萬能判斷
    var a = new Array(); console.log(a instanceof Array) // a是否Array的實例 true console.log(a.constructor == Array) // a實例所對應的構造函數是否爲Array true // constructor屬性是能夠被修改的,會致使檢測出的結果不正確 function Dog(){ } function Cat(){ } Cat.prototype = new Dog(); var m= new Cat(); console.log(m.constructor==Cat); // false console.log(John.constructor==Person); // true // instanceof 對於直接或間接引用都是true console.log(m instanceof Cat); // true console.log(John instanceof Person); // true //Object.prototype.toString.call function a() { }; var toString = Object.prototype.toString; console.log(toString.call(new Date) === '[object Date]'); //true console.log(toString.call(new String) ==='[object String]');//true console.log(toString.call(a) ==='[object Function]'); //true //$.type jQuery.type( undefined ) === "undefined" // true jQuery.type() === "undefined" // true jQuery.type( null ) === "null" // true jQuery.type( true ) === "boolean" // true

1.1.3. 函數

  • 普通函數 -- 直接調用
  • 構造函數 -- new 建立對象
  • 對象方法 -- 對象調用

1.1.4. 內置對象

  • window
    • 全局對象,主要描述瀏覽器窗口相關的屬性和狀態
  • Date
  • Array
  • JSON
    • 主要用於對象的序列化和反序列化
    • 實現對象的深拷貝
  • RegExp
-- 淺複製與深拷貝
  1. 淺複製
    對對象地址的複製,並無開闢新的棧,複製的結果是兩個對象指向同一個地址,修改其中一個對象的屬性,則另外一個對象的屬性也會改變
  2. 深拷貝
    開闢新的棧,兩個對象對應兩個不一樣的地址,修改一個對象的屬性,不會改變另外一個對象的屬性

方法

  1. 遞歸

    var china = { nation : '中國', birthplaces:['北京','上海','廣州'], skincolr :'yellow', friends:['sk','ls'] } //深複製,要想達到深複製就須要用遞歸 function deepCopy(o,c){ var c = c || {} for(var i in o){ if(typeof o[i] === 'object'){ //要考慮深複製問題了 if(o[i].constructor === Array){ //這是數組 c[i] =[] }else{ //這是對象 c[i] = {} } deepCopy(o[i],c[i]) }else{ c[i] = o[i] } } return c } var result = {name:'result'} result = deepCopy(china,result) console.dir(result)

  2. JSON

    var test ={ name:{ xing:{ first:'張', second:'李' }, ming:'老頭' }, age :40, friend :['隔壁老王','宋經紀','同事'] } var result = JSON.parse(JSON.stringify(test)) result.age = 30 result.name.xing.first = '往' result.friend.push('fdagldf;ghad') console.dir(test) console.dir(result)

1.2. 函數原型鏈

JS是一種基於對象的語言,但在ES6 以前是不支持繼承的,爲了具有繼承的能力,Javascript 在 函數對象上創建了原型對象prototype,並以函數對象爲主線,從上至下,在JS內部構建了一條 原型鏈

Object 是全部對象的祖宗, 任何對象所創建的原型鏈最終都指向了Object

簡單來講:
創建了變量查找機制,當訪問一個對象的屬性時,先查找對象自己是否存在,若是不存在就去該對象所在的原型連上去找,直到Object對象爲止,若是都沒有找到該屬性纔會返回undefined。所以,咱們能夠經過原型鏈來實現JS繼承

1.3. 函數做用域

變量在聲明它們的函數體以及這個函數體嵌套的任意函數體

JS中沒有塊級做用域,只有函數做用域
致使JS中出現了變量提高的問題
—— 將變量聲明提高到它所在做用域的最開始的部分
爲了解決變量提高帶來的反作用,ES6新增了let 命令來聲明變量,let 所聲明的變量只在 let 命令所在的代碼塊內有效,因此不存在變量提高問題

1.4. this 指針

this 指針存在於函數中,用以標識函數運行時所處的上下文

  • 普通函數
    始終指向全局對象window
  • 構造函數
    指向新建立的對象
  • 方法
    指向調用該方法的對象
  • call、apply 和 bind
    方法來改變函數的 this 指向,其中,call 和 apply 主動執行函數,bind通常在事件回調中使用, call 和 apply的區別只是參數的傳遞方式不一樣

1.5. new 操做符

函數的建立有三種方式,即 顯式聲明、匿名定義 和 new Function()

JS將新對象的原型鏈指向了構造函數的原型對象,因而就在新對象和函數對象之間創建了一條原型鏈,經過新對象能夠訪問到函數對象原型prototype中的方法和屬性

1.6. 閉包

具備獨立做用域的靜態執行環境

和函數做用域不一樣的是:

  • 閉包的做用域
    靜態的,能夠永久保存局部資源
  • 函數做用域
    只存在於運行時,函數執行結束後當即銷燬

所以,閉包能夠造成一個獨立的執行過程

1.7. 單線程和異步

JavaScript

  • 單線程語言,在瀏覽器中,當JS代碼被加載時,瀏覽器會爲其分配一個主線程來執行任務(函數)
    主線程會造成一個全局執行環境,執行環境在棧中採用後進先出(LIFO)的順序來執行代碼塊,以保證全部的函數能按照正確的順序被執行
  • 執行環境中維護了一個異步隊列(也叫工做線程),並將這些耗時任務放入隊列中進行等待
    • 如ajax請求、定時器、事件等
    • 這些任務的執行時機並不肯定,只有當主線程的任務執行完成之後,主線程纔會去檢查異步隊列中的任務是否須要開始執行。
    • JS中的 setTimeout 和 setInterval 就是典型的異步操做,它們會被放入異步隊列中等待,即便 setTimeout(0)也不會被當即執行,須要等到當前同步任務結束後纔會被執行。

1.8. 異步通訊

瀏覽器專門用來和服務器進行交互的異步通信技術

1.8.1. Ajax
  • Ajax是瀏覽器專門用來和服務器進行交互的異步通信技術
  • 其核心對象是XMLHttpRequest,經過該對象能夠建立一個Ajax請求
  • Ajax請求是一個耗時的異步操做,當請求發出之後,Ajax提供了兩個狀態位來描述請求在不一樣階段的狀態,這兩個狀態位分別是
    • readyState
    • status
      readyState 經過5個狀態碼來描述一個請求的5個階段:
      0 - 請求未發送,初始化階段
      1 - 請求發送中,服務器還未收到請求
      2 - 請求發送成功,服務器已收到請求
      3 - 服務器處理完成,開始響應請求,傳輸數據
      4 - 客戶端收到請求,並完成了數據下載,生成了響應對象
      status
      • 1xx(臨時響應)表示臨時響應並須要請求者繼續執行操做的狀態碼。
        • 2xx(成功)表示成功處理了請求的狀態碼。
        • 200(成功):服務器已成功處理了請求。一般,這表示服務器提供了請求的網頁。
      • 3xx(重定向)要完成請求,須要進一步操做。
        • 301(永久移動):請求的網頁已永久移動到新位置。
        • 302(臨時移動):服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來響應之後的請求。
        • 304(未修改):自從上次請求後,請求的網頁未修改過。
      • 4xx(請求錯誤)這些狀態碼錶示請求可能出錯,妨礙了服務器的處理。
        • 400(錯誤請求):服務器不理解請求的語法。
        • 404(未找到):服務器找不到請求的網頁。
      • 5xx(服務器錯誤)這些狀態碼錶示服務器在處理請求時發生內部錯誤。
        • 500(服務器內部錯誤):服務器遇到錯誤,沒法完成請求。
        • 503(服務不可用):服務器目前沒法使用(因爲超載或停機維護)

常見問題:

  • timeout 只會影響readyState,而不會影響status,由於超時只會中斷數據傳輸,但不會影響服務器的處理結果。 若是 timeout 設置的不合理,就會致使響應碼status 是200,但 response裏卻沒有數據,這種狀況就是服務器正確響應了請求,但數據的下載被超時中斷了。

HTTP 相關請見:

只容許請求和當前地址同域的服務器資源。但不限制腳本和標籤發送跨域請求,好比script 和 img 標籤,所以能夠利用腳本跨域能力來實現跨域請求,即JSONP 的原理。

JSONP雖然能夠解決跨域問題,但只能是get請求,而且沒有有效的錯誤捕獲機制
爲了解決這個問題,XMLHttpRequest Level2 提出了CORS 模型,即 跨域資源共享, 它不是一個新的API,而是一個標準規範,當瀏覽器發現該請求須要跨域時,就會自動在頭信息中添加一個 Origin字段,用以說明本次請求來自哪一個源。服務器根據這個值,決定是否贊成此次請求。

隨着移動端的快速發展,Web技術的應用場景正在變得愈來愈複雜, 關注點分離 原則在系統設計層面就顯得愈來愈重要,而XMLHttpRequest 是 Ajax 最古老的一個接口,於是不太符合現代化的系統設計理念。所以,瀏覽器提供了一個新的 Ajax 接口,即 Fetch API ,Fetch API 是基於Promise 思想設計的,更符合關注點分離原則。

更多請見:

1.9. 模塊化

模塊加載方案,最主要有 CMD 和 AMD 兩種,分別以commonjs 和 requirejs爲表明

ES6 在語言標準的層面上,實現了模塊化編程,其設計思想是,儘可能靜態化,使得編譯時就能肯定模塊的依賴關係,即編譯時加載

CMD和AMD是在運行時肯定依賴關係,即運行時加載

詳情:
AMD && CMD

ES6 模塊化

每個ES6模塊都是一個包含JS代碼的文件,模塊本質上就是一段腳本,而不是用module關鍵字定義一個模塊,可是模塊與腳本仍是有兩點區別:

  • 在ES6模塊中,不管你是否加入「use strict;」語句,默認狀況下模塊都是在嚴格模式下運行。
  • 在模塊中你能夠使用import和export關鍵字。

默認狀況下,你在模塊中的全部聲明相對於模塊而言都是寄存在本地的。若是你但願公開在模塊中聲明的內容,並讓其它模塊加以使用,你必定要導出這些功能。想要導出模塊的功能有不少方法,其中最簡單的方式是添加export關鍵字,能夠導出全部的最外層函數、類以及var、let或const聲明的變量。

es6中 代碼就是模塊,不是一段腳本,因此全部的聲明都被限定在模塊的做用域中,對全部腳本和模塊全局不可見。你須要作的是將組成模塊公共API的聲明所有導出。

webpack
在編譯時計算全部依賴並將全部模塊打包成一個文件,經過網絡一次傳輸全部模塊
減小加載模塊時的網絡往返時間

深刻淺出ES6(模塊):http://www.infoq.com/cn/articles/es6-in-depth-modules

1.10. Node.js

一個基於 Chrome V8 引擎的 JavaScript運行環境

Node.js在服務端的優點是,它採用單線程和異步I/O模型,實現了一個高併發、高性能的運行時環境。相比傳統的多線程模型,Node.js實現簡單,而且能夠減小資源開銷

1.11. ES6

目標是讓JS可以方便的開發企業級大型應用程序

變化

  • 新增 let、const 命令 來聲明變量
    和var 相比,let聲明的變量不存在變量提高問題,但沒有改變JS弱類型的特色,依然能夠接受任意類型變量的聲明;const
    聲明的變量不容許在後續邏輯中改變,提升了JS語法的嚴謹性。
  • 新增解構賦值、rest語法、箭頭函數
    這些都是爲了讓代碼看起來更簡潔,而包裝的語法糖。
  • 新增模塊化
    這是JS走向規範比較重要的一步,讓前端更方便的實現工程化。
  • 新增類和繼承的概念
    配合模塊化,JS也能夠實現高複用、高擴展的系統架構。
  • 新增模板字符串功能
    高效簡潔,結束拼接字符串的時代。
  • 新增Promise對象
    解決異步回調多層嵌套的問題
    使得本來的多層級的嵌套代碼,變成了鏈式調用 讓代碼更清晰,減小嵌套數

promise

  • 容器:裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果
  • 對象:從它能夠獲取異步操做的消息

特色

  • 對象的狀態不受外界影響
    Promise對象表明一個異步操做,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。
  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果
    Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。

缺點

  • 沒法取消Promise
    一旦新建它就會當即執行,沒法中途取消
  • 若是不設置回調函數,Promise內部拋出的錯誤,不會反應到外部
  • 當處於Pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)

更多:

2. CSS

2.1. CSS 選擇器

  • 類選擇器
  • 標籤選擇器
  • ID選擇器
  • 後代選擇器
  • 羣組選擇器
  • 僞類選擇器(before/after)
  • 兄弟選擇器(+~)
  • 屬性選擇器等等

2.2. 盒子模型

  • 塊級盒子(block)
  • 行內盒子(inline-block)

相關屬性
margin、border、padding、content

注意

  • 只有普通文檔流中塊級盒子的垂直外邊距纔會發生合併,而行內盒子、浮動盒子或絕對定位之間的外邊距不會合並
    根據規範,一個盒子若是沒有上補白(padding-top)和上邊框(border-top),那麼這個盒子的上邊距會和其內部文檔流中的第一個子元素的上邊距重疊
    爲父元素增長一個border-top或者padding-top便可解決這個問題
  • box-sizing 屬性的設置會影響盒子width和height的計算

更多:

2.3. 浮動佈局

設置元素的 float 屬性,能使該元素脫離普通文檔流
若是子元素所有設置爲浮動,則父元素是塌陷的

  • 清除浮動
    clear:both,
  • BFC
    浮動元素的父元素 + overflow:hidden 樣式
  • 行內盒子(inline-block)
  • table也能夠實現一樣的效果。

2.4. 定位佈局

脫離文檔流:position 值爲 relative/absolute/fixed

  • relative
    相對定位,它以本身原來的位置進行偏移,偏移後,原來的空間不會被其餘元素佔用
  • absolute
    絕對定位,它以離本身最近的定位父容器做爲參照進行偏移
    經常使用的方式就是設置父容器的poistion:relative
  • fixed
    固定定位,以瀏覽器窗口爲參照物
    PC網頁底部懸停的banner通常均可以經過fixed定位來實現,但fixed屬性在移動端有兼容性問題,所以不推薦使用,可替代的方案是:絕對定位+內部滾動。

更多:

2.5. 彈性佈局

即Flex佈局,定義了flex的容器一個可伸縮容器

  • 容器自己會根據容器中的元素動態設置自身大小
  • 當Flex容器被應用一個大小時(width和height),將會自動調整容器中的元素適應新大小
  • Flex容器也能夠設置伸縮比例和固定寬度,還能夠設置容器中元素的排列方向(橫向和縱向)和是否支持元素的自動換行
    容器的屬性
    • flex-direction屬性
    • flex-wrap屬性
    • flex-flow
    • justify-content屬性
    • align-items屬性
    • align-content屬性
      項目的屬性
    • order屬性
    • flex-grow屬性
    • flex-shrink屬性
    • flex-basis屬性
    • flex屬性
    • align-self屬性
  • 注意,設爲Flex佈局之後,子元素的float、clear和vertical-align屬性將失效。

更多: Flex 佈局

2.6. CSS3 動畫

  • transition
    讓元素的CSS屬性值的變化在一段時間內平滑的過渡
    CSS3引入了transfrom屬性,它能夠經過對元素進行 平移(translate)、旋轉(rotate)、放大縮小(scale)、傾斜(skew)
    等操做,來實現2D和3D變換效果
    transiton 還有一個結束事件 transitionEnd,該事件是在CSS完成過渡後觸發,若是過渡在完成以前被移除,則不會觸發transitionEnd
  • animation
    須要設置一個@keyframes,來定義元素以哪一種形式進行變換
    而後再經過動畫函數讓這種變換平滑的進行,從而達到動畫效果
    • 動畫能夠被設置爲永久循環演示
    • animation-play-state:paused能夠暫停動畫
    • animation-fill-mode:forwards 可讓動畫完成後定格在最後一幀
    • 能夠經過JS監聽animation的開始、結束和重複播放時的狀態,分別對應三個事件,即 animationStart、animationEnd、animationIteration
      注意,當播放次數設置爲1時,不會觸發 animationIteration

對比

  • animation 設置動畫效果更靈活更豐富
  • transition 只能經過主動改變元素的css值才能觸發動畫效果,而animation一旦被應用,就開始執行動畫

2.7. BFC

BFC---Block Formatting Context

是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面元素

好比:內部滾動就是一個BFC,當一個父容器的overflow-y設置爲auto時,而且子容器的長度大於父容器時,就會出現內部滾動,不管內部的元素怎麼滾動,都不會影響父容器之外的佈局,這個父容器的渲染區域就叫BFC。

特色

  • 盒子們自所在的containing block頂部一個接一個垂直排列
  • 水平方向上撐滿整個寬度(除非內部盒子本身創建了新的BFC)
  • 兩個相鄰的BFC之間的距離由margin決定
  • 在同一個BFC內部,兩個垂直方向相鄰的塊級元素的margin會發生「塌陷」

觸發BFC
根元素或其它包含它的元素

  • float的值不爲none
  • overflow的值不爲visible
  • display的值爲inline-block、table-cell、table-caption
  • position的值爲absolute或fixed
  • flex boxes (元素的display: flex或inline-flex)

應用

  • 清除內部浮動
    對子元素設置浮動後,父元素會發生高度塌陷,也就是父元素的高度變爲0。解決這個問題,只須要把把父元素變成一個BFC就好了。經常使用的辦法是給父元素設置overflow:hidden
  • 垂直margin合併
    屬於同一個BFC的兩個相鄰元素的margin會發生重疊 —— 建立 BFC
  • 建立自適應兩欄佈局 —— 解決侵佔浮動元素的問題

參考: CSS: 潛藏着的BFC

2.8. Sprite,Iconfont,@font-face

  • Sprite圖
    爲了減小http請求的次數,通常會將經常使用的小圖標排到一個大圖中,頁面加載時只需請求一次網絡,在css中經過設置background-position來控制顯示所須要的小圖標
  • Iconfont
    即字體圖標,就是將經常使用的圖標轉化爲字體資源存在文件中,經過在CSS中引用該字體文件,而後能夠直接用控制字體的css屬性來設置圖標的樣式
    字體圖標的好處是節省網絡請求、其大小不受屏幕分辨率的影響,而且能夠任意修改圖標的顏色
  • @font-face
    是CSS3中的一個模塊
    經過@font-face能夠定義一種全新的字體,經過css屬性font-family來使用這個字體

3. HTML

3.1. BOM

Browser Object Model 瀏覽器對象模型

當一個瀏覽器頁面初始化時,會在內存建立一個全局的對象,用以描述當前窗口的屬性和狀態,這個全局對象被稱爲瀏覽器對象模型,即BOM

BOM的核心對象就是window,window對象也是BOM的頂級對象,其中包含了瀏覽器的 6個核心模塊:

  • document
    • 即文檔對象,渲染引擎在解析HTML代碼時,會爲每個元素生成對應的DOM對象,因爲元素之間有層級關係,所以整個HTML代碼解析完之後,會生成一個由不一樣節點組成的樹形結構,俗稱DOM樹
    • document用於描述DOM樹的狀態和屬性,並提供了不少操做DOM的API。
  • frames
    • HTML 子框架,即在瀏覽器裏嵌入另外一個窗口
    • 父框架和子框架擁有獨立的做用域和上下文。
  • history
    • 以棧(FIFO)的形式保存着頁面被訪問的歷史記錄
    • 頁面前進即入棧,頁面返回即出棧。
  • location
    • 提供了當前窗口中加載的文檔相關信息以及一些導航功能
  • navigator
    • 用來描述瀏覽器自己,包括瀏覽器的名稱、版本、語言、系統平臺、用戶特性字符串等信息
  • screen
    • 提供了瀏覽器顯示屏幕的相關屬性,好比顯示屏幕的寬度和高度,可用寬度和高度。

3.2. DOM 系統

Document Object Model 文檔對象模型,是全部瀏覽器公共遵照的標準

  • DOM將HTML和XML文檔映射成一個由不一樣節點組成的樹型結構,俗稱DOM樹
  • 其核心對象是document,用於描述DOM樹的狀態和屬性,並提供對應的DOM操做API

3.3. 事件系統

事件是用戶與頁面交互的基礎,到目前爲止,DOM事件從PC端的 鼠標事件(mouse) 發展到了 移動端的 觸摸事件(touch) 和
手勢事件(guesture),touch事件描述了手指在屏幕操做的每個細節,guesture 則是描述多手指操做時更爲複雜的狀況

總結以下:

  • 第一根手指放下,觸發 touchstart,除此以外什麼都不會發生
  • 手指滑動時,觸發touchmove
  • 第二根手指放下,觸發 gesturestart
  • 觸發第二根手指的 touchstart
  • 當即觸發 gesturechange
  • 任意手指移動,持續觸發 gesturechange
  • 第二根手指彈起時,觸發 gestureend,之後將不會再觸發 gesturechange
  • 觸發第二根手指的 touchend
  • 觸發touchstart (多根手指在屏幕上,提起一根,會刷新一次全局touch)
  • 彈起第一根手指,觸發 touchend

DOM2.0 模型將事件處理流程分爲三個階段,即 事件捕獲階段 、 事件處理階段 、 事件冒泡階段

  • 事件捕獲
    當用戶觸發點擊事件後,頂層對象document 就會發出一個事件流,從最外層的DOM節點向目標元素節點傳遞,最終到達目標元素。
  • 事件處理
    當到達目標元素以後,執行目標元素綁定的處理函數。若是沒有綁定監聽函數,則不作任何處理。
  • 事件冒泡
    事件流從目標元素開始,向最外層DOM節點傳遞,途中若是有節點綁定了事件處理函數,這些函數就會被執行。

利用事件冒泡原理能夠實現 事件委託

所謂事件委託,就是在父元素上添加事件監聽器,用以監聽和處理子元素的事件,避免重複爲子元素綁定相同的事件

  • 方式
    當目標元素的事件被觸發之後,這個事件就從目標元素開始,向最外層元素傳遞,最終冒泡到父元素上,父元素再經過event.target獲取到這個目標元素
  • 好處
    父元素只需綁定一個事件監聽,就能夠對全部子元素的事件進行處理了,從而減小了沒必要要的事件綁定,對頁面性能有必定的提高。

更多: 事件委託和 this

3.4. HTML 解析過程

瀏覽器加載 html 文件之後,渲染引擎會從上往下,一步步來解析HTML標籤

過程以下:

  • 請求服務器返回HTML文件
    用戶輸入網址,瀏覽器向服務器發出請求,服務器返回html文件;
  • 生成dom 樹
    渲染引擎開始解析 html 標籤,並將標籤轉化爲DOM節點,生成 DOM樹;
  • css文件請求
    若是head 標籤中引用了外部css文件,則發出css文件請求,服務器返回該文件,該過程會阻塞後面的解析;
  • js 請求
    若是引用了外部 js 文件,則發出 js 文件請求,服務器返回後當即執行該腳本,這個過程也會阻塞html的解析;
  • 生成渲染樹
    引擎開始解析 body 裏面的內容,若是標籤裏引用了css 樣式,就須要解析剛纔下載好的css文件,而後用css來設置標籤的樣式屬性,並生成渲染樹;
  • 下載圖片資源
    若是 body 中的 img 標籤引用了圖片資源,則當即向服務器發出請求,此時引擎不會等待圖片下載完畢,而是繼續解析後面的標籤;
  • 從新渲染
    • 服務器返回圖片文件,因爲圖片須要佔用必定的空間,會影響到後面元素的排版,所以引擎須要從新渲染這部份內容;
    • 若是此時 js 腳本中運行了 style.display="none",佈局被改變,引擎也須要從新渲染這部分代碼;
  • 直到 html 結束標籤爲止,頁面解析完畢。

3.5. 重繪 和 迴流

  • 迴流
    當渲染樹中的一部分(或所有)由於元素的規模尺寸,佈局,隱藏等改變而須要從新構建
    如上面的img文件加載完成後就會引發迴流,每一個頁面至少須要一次迴流,就是在頁面第一次加載的時候
  • 重繪
    當渲染樹中的一些元素須要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響佈局的,好比 background-color

從上面能夠看出,迴流必將引發重繪,而重繪不必定會引發迴流

會引發重繪和迴流的操做以下:

  • 添加、刪除元素(迴流+重繪)
  • 隱藏元素,display:none(迴流+重繪),visibility:hidden(只重繪,不迴流)
  • 移動元素,好比改變top,left的值,或者移動元素到另一個父元素中。(重繪+迴流)
  • 對style的操做(對不一樣的屬性操做,影響不同)
  • 還有一種是用戶的操做,好比改變瀏覽器大小,改變瀏覽器的字體大小等(迴流+重繪)
  • 另外,transform操做不會引發重繪和迴流,是一種高效率的渲染。這是由於transform屬於合成屬性,對合成屬性進行 transition/animation 動畫時將會建立一個合成層,這使得動畫元素在一個獨立的層中進行渲染,當元素的內容沒有發生改變,就不必進行重繪,瀏覽器會經過從新複合來建立動畫幀。

3.6. 本地存儲

避免取回數據前頁面空白,減小請求服務器次數

  • cookie
    • 本地存儲最原始的方式
      cookie 是存放在本地瀏覽器的一段文本,數據以鍵值對的形式保存,能夠設置過時時間。
    • 不適合大量數據的存儲
      由於每請求一次頁面,cookie 都會發送給服務器,這使得 cookie速度很慢並且效率也不高。所以cookie的大小被限制爲4k左右(不一樣瀏覽器可能不一樣,分HOST)
  • html5 提供了兩種在客戶端存儲數據的新方法:
    • localStorage
      永久存儲
    • sessionStorage
      存儲期限僅限於瀏覽器會話(session),即當瀏覽器窗口關閉後,sessionStorage中的數據被清除
    都是以key/value的形式來存儲數據
    localStorage的存儲空間大約5M左右(不一樣瀏覽器可能不一樣,分 HOST),這個至關於一個5M大小的前端數據庫,相比於cookie,能夠節約帶寬,但localStorage在瀏覽器隱私模式下是不可讀取的,當存儲數據超過了 localStorage 的存儲空間後會拋出異常。
    此外,H5還提供了逆天的 websql 和 indexedDB,容許前端以關係型數據庫的方式來存儲本地數據

cookie做用是與服務器交互,做爲HTTP規範的一部分,web storage僅僅爲本地存儲而生

更多:經常使用的 web 客戶端存儲

3.7. 瀏覽器緩存機制

瀏覽器緩存機制是指經過 HTTP 協議頭裏的 Cache-Control (或 Expires) 和 Last-Modified (或 Etag) 等字段來控制文件緩存的機制。

  • Cache-Control
    • 用於控制文件在本地緩存有效時長
    • 好比服務器回包:Cache-Control:max-age=600
      表示文件在本地應該緩存,且有效時長是600秒 (從發出請求算起)。在接下來600秒內,若是有請求這個資源,瀏覽器不會發出 HTTP請求,而是直接使用本地緩存的文件。
  • Last-Modified
    • 標識文件在服務器上的最新更新時間
      下次請求時,若是文件緩存過時,瀏覽器經過 If-Modified-Since 字段帶上這個時間,發送給服務器,由服務器比較時間戳來判斷文件是否有修改。若是沒有修改,服務器返回304告訴瀏覽器繼續使用緩存;若是有修改,則返回200,同時返回最新的文件。
  • Cache-Control 一般與 Last-Modified 一塊兒使用
    一個用於控制緩存有效時間,一個在緩存失效後,向服務查詢是否有更新。
    • Cache-Control 還有一個同功能的字段:Expires。Expires 的值爲一個絕對的時間點
      如:Expires: Thu, 10 Nov 2015 08:45:11 GMT,表示在這個時間點以前,緩存都是有效的。
  • Etag 也是和 Last-Modified 同樣,對文件進行標識的字段
    不一樣的是,Etag 的取值是一個對文件進行標識的特徵字串。在向服務器查詢文件是否有更新時,瀏覽器經過 If-None-Match
    字段把特徵字串發送給服務器,由服務器和文件最新特徵字串進行匹配,來判斷文件是否有更新。沒有更新回包304,有更新回包200。Etag 和 Last-Modified 可根據需求使用一個或兩個同時使用。兩個同時使用時,只要知足基中一個條件,就認爲文件沒有更新。

另外有兩種特殊的狀況:

  • 手動刷新頁面(F5)
    瀏覽器會直接認爲緩存已通過期(可能緩存尚未過時),在請求中加上字段:Cache-Control:max-age=0,發包向服務器查詢是否有文件是否有更新。
  • 強制刷新頁面(Ctrl+F5)
    瀏覽器會直接忽略本地的緩存(有緩存也會認爲本地沒有緩存),在請求中加上字段:Cache-Control:no-cache (或 Pragma:no-cache),發包向服務從新拉取文件

3.8. History

用戶訪問網頁的歷史記錄一般會被保存在一個相似於棧的對象中,即history對象,點擊返回就出棧,跳下一頁就入棧

它提供瞭如下方法來操做頁面的前進和後退:

  • window.history.back( ) 返回到上一個頁面
  • window.history.forward( ) 進入到下一個頁面
  • window.history.go( [delta] ) 跳轉到指定頁面

HTML5 對History Api 進行了加強,新增了兩個Api 和一個事件,分別是pushState、replaceState 和 onpopstate:

  • pushState是往history對象裏添加一個新的歷史記錄,即壓棧。
  • replaceState 是替換history對象中的當前歷史記錄。
  • 當點擊瀏覽器後退按鈕或 js調用history.back 都會觸發 onpopstate 事件。
    與其相似的還有一個事件:onhashchange,onhashchange是老API,瀏覽器支持度高,原本是用來監聽hash變化的,但能夠被利用來作客戶端前進和後退事件的監聽,而onpopstate是專門用來監聽瀏覽器前進後退的,不只能夠支持hash,非hash的同源 url 也支持。

3.9. HTML5離線緩存

HTML5離線緩存又叫Application Cache,是從瀏覽器的緩存中分出來的一塊緩存區,若是要在這個緩存中保存數據,能夠使用一個描述文件(manifest file),列出要下載和緩存的資源。

manifest 文件是簡單的文本文件,它告知瀏覽器被緩存的內容(以及不緩存的內容)。manifest 文件可分爲三個部分:

  • CACHE MANIFEST - 在此標題下列出的文件將在首次下載後進行緩存
  • NETWORK - 在此標題下列出的文件須要與服務器的鏈接,且不會被緩存
  • FALLBACK - 在此標題下列出的文件規定當頁面沒法訪問時的回退頁面(好比 404 頁面)

離線緩存爲應用帶來三個優點:

  • 離線瀏覽 - 用戶可在應用離線時使用它們
  • 速度 - 已緩存資源加載得更快
  • 減小服務器負載 - 瀏覽器將只從服務器下載更新過或更改過的資源。

參考:

 
let //普通變量
const //靜態變量
import //模塊
class //類

const 關鍵字用於聲明一個不可從新聲明和賦值的常量,MDN: const
 
 
 
 

Web Storage

 

 

經常使用的web客戶端存儲

1. 引言

本地存儲:避免取回數據前頁面空白,減小請求服務器次數 chrome瀏覽器,查看resources 

2. 經常使用存儲方案

2.1. indexDB

相似SQL數據庫的結構化數據存儲機制,可以在客戶端存儲大量的結構化數據 缺點: 兼容性很差,瀏覽器支持度低

2.2. cookie

HTTP cookie 「瀏覽器」提供的一種機制,經過document.cookie訪問

  • 既能夠服務器端設置,也能夠客戶端設置,會跟隨任意HTTP請求發送
  • cookie是存於用戶硬盤的一個文件,這個文件一般對應於一個域名,也就是說,cookie能夠跨越一個域名下的多個網頁,但不能跨越多個域名使用

cookie_limit!(/img/in-post/post-js-storage/cookie_limit.png)![]

2.2.1. 用途
  1. 保存用戶信息
  2. 購物車
  3. 跟蹤用戶行爲
2.2.2. 查找過程
  1. 用戶訪問站點
  2. web應用讀取cookie包含的信息
  3. 再次訪問時,瀏覽器在本地硬盤上查找相關cookie
  4. 若存在該cookie,添加到request header cookie字段中,與該HTTP請求一塊兒發送 存儲在cookie的數據每次都會自動添加到請求中,濫用則下降性能 cookie
2.2.3. cookie相關屬性

resources_cookie

  1. domain 和 path 共同決定了cookie的共享頁面

    例如cookie設置爲"domain=.google.com.hk; path=/webhp",那麼只有".google.com.hk/webhp"及"/webhp"下的任一子目錄如"/webhp/aaa"或"/webhp/bbb"會發送cookie信息,而".google.com.hk"就不會發送,即便它們來自同一個域。

  2. expries 和 max-age 是用來決定cookie的生命週期

  3. secure cookie的安全標誌 cookie中惟一一個非名值對兒的部分,默認爲空,不管是 http 請求仍是 https 請求,均會發送cookie。 指定後,cookie只有在使用SSL鏈接(如HTTPS請求)時纔會發送到服務器

  4. httponly 服務端設置 限制客戶端腳本對cookie的訪問,將 cookie 設置成 httponly 能夠減輕xss攻擊的危害

2.2.4. cookie設置
  1. 服務器端 經過發送一個名爲 Set-Cookie 的HTTP頭來建立一個cookie,做爲 Response Headers 的一部分,每一個Set-Cookie 表示一個 cookie(若是有多個cookie,需寫多個Set-Cookie),每一個屬性也是以名/值對的形式(除了secure),屬性間以分號加空格隔開。格式以下: Set-Cookie: name=value[; expires=GMTDate][; domain=domain][; path=path][; secure] cookie_server

只有name,value發送到服務器,其餘是給瀏覽器的指示 2. 客戶端 document.cookie = "name=value[; expires=GMTDate][; domain=domain][; path=path][; secure]"

var cookie = { /**設置cookie ** name 標識 ** value 值 ** options { ** 'path': '訪問路徑', ** 'domain' : '域名', ** 'expire' : 過時時間 } **/ setCookie : function(name,value,options){ var options = options ? options : {}, path = options.path ? options.path : '/', domain = options.domain ? options.domain : document.domain, time = options.expire ? (new Date().getTime() + options.expire * 1000) : '', expire = new Date(time).toUTCString(); document.cookie = encodeURIComponent(name) + "="+ encodeURIComponent(value) + ";expires=" + expire + ";domain=" + domain + ";path=" + path; }, //獲取cookie getCookie: function(name){ var arr, reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)"); if(arr=document.cookie.match(reg)){ console.log(arr); return unescape(arr[2]); } return null; }, //移除cookie removeCookie: function(name){ var val = this.getCookie(name); if(val != null){ this.setCookie(name,val, { expire : - 1 }) } } } 
2.2.5. 優缺點

優勢: 兼容性好 缺點:

  1. 增長了網絡流量
  2. 數據容量有限(最多4kb,瀏覽器間有區別)
  3. 安全性

2.3. sessionStorage和localStorage

key value形式存儲

storage_support

  • localStorage - 沒有時間限制的數據存儲
  • sessionStorage - 針對一個 session 的數據存儲
if(typeof(Storage)!=="undefined") { // 是的! 支持 localStorage sessionStorage 對象! // 一些代碼..... } else { // 抱歉! 不支持 web 存儲。 } 
2.3.1. 使用

localStorage.sitename = "超然haha"; localStorage.removeItem("lastname");

  • 保存數據: localStorage.setItem(key,value);
  • 讀取數據: localStorage.getItem(key);
  • 刪除單個數據: localStorage.removeItem(key);
  • 刪除全部數據: localStorage.clear();
  • 獲得某個索引的key:localStorage.key(index);
2.3.2. 適用範圍
  • 不須要和服務器進行交互的一些數據 好比導航欄當前的狀態,一些普通的數據進行緩存。甚至咱們能夠存儲html片斷,js或者css文件段
  • 不少應用經過版本控制來存儲一些不常常改動的js/css文件。減小用戶請求帶寬的同時優化整個頁面的加載速度。
2.3.3. 注意

localstorage存儲的值只能是字符串的形式 當咱們存儲數據爲引用對象的時候,會默認調用對象的toString方法,轉化爲字符串存儲 因此咱們在存儲數組時,存儲的數據會將數據項以,隔開,解析的時候須要咱們分解成爲數組再操做。而對於對象,咱們須要用JSON.stringify轉化存儲,獲取數據後再用JSON.parse轉化爲對象

2.4. web storage 與 cookie

web storage優勢:

  1. web storage 爲了更大容量存儲,通常限制爲同一域名5M,,而且不一樣域名的數據不能相互訪問
  2. localStorage是存儲在用戶本地的瀏覽器上,不像cookie同樣攜帶在http請求頭部的字段裏面,這有效的節約了帶寬
  3. cookie須要指定做用域,不可跨域調用
  4. 擁有setItem,getItem,removeItem,clear等方法,不像cookie須要前端開發者本身封裝setCookie,getCookie

cookie優勢:

  1. cookie做用是與服務器交互,做爲HTTP規範的一部分,web storage僅僅爲本地存儲而生

參考:經常使用的本地存儲——cookie篇

 

 

 

 

You don't know js

 

 

「Give me a chance to know you. 」

更多內容: 移步這裏

1. 做用域

1.1. 編譯原理

儘管一般將 JavaScript 歸類爲「動態」 或「解釋執行」 語言, 但事實上它是一門編譯語言。
程序中的一段源代碼在執行以前會經歷三個步驟, 統稱爲「編譯」

  1. 分詞/詞法分析(Tokenizing/Lexing)
    • 這個過程會將由字符組成的字符串分解成(對編程語言來講) 有意義的代碼塊, 這些代
      碼塊被稱爲詞法單元(token)。 例如, 考慮程序 var a = 2;。 這段程序一般會被分解成
      爲下面這些詞法單元: var、 a、 =、 2 、 ;。
  2. 解析/語法分析(Parsing)
    • 這個過程是將詞法單元流(數組) 轉換成一個由元素逐級嵌套所組成的表明了程序語法
      結構的樹。 這個樹被稱爲「抽象語法樹」(Abstract Syntax Tree, AST)。
  3. 代碼生成
    • 將 AST 轉換爲可執行代碼的過程稱被稱爲代碼生成。

1.2. 做用域嵌套

當一個塊或函數嵌套在另外一個塊或函數中時, 就發生了做用域的嵌套。 所以, 在當前做用
域中沒法找到某個變量時, 引擎就會在外層嵌套的做用域中繼續查找, 直到找到該變量,
或抵達最外層的做用域(也就是全局做用域) 爲止。
將做用域處理的過程可視化,以下面的建築:

做用域是一套規則, 用於肯定在何處以及如何查找變量(標識符)。

2. 詞法做用域

做用域共有兩種主要的工做模型:

  • 詞法做用域(重點討論)
  • 動態做用域(如bash腳本,perl中的一些模式)

2.1. 詞法階段

詞法化的過程會對源代碼中的字符進行檢查,若是是有狀態的解析過程,還會賦予單詞語義——名稱來歷

詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏來決定的

如:

function foo(a) { var b = a * 2; function bar(c) { console.log( a, b, c ); } bar( b * 3 ); } foo( 2 ); // 2, 4, 12

能夠將以上代碼想象成幾個逐級包含的氣泡

① 包含着整個全局做用域, 其中只有一個標識符: foo。
② 包含着 foo 所建立的做用域, 其中有三個標識符: a、 bar 和 b。
③ 包含着 bar 所建立的做用域, 其中只有一個標識符: c。
做用域氣泡由其對應的做用域塊代碼寫在哪裏決定, 它們是逐級包含的。

查找

做用域氣泡的結構和互相之間的位置關係給引擎提供了足夠的位置信息,做用域查找會在找到第一個匹配的標識符時中止

全局變量會自動成爲全局對象(好比瀏覽器中的window對象)的屬性,所以能夠不直接經過全局對象的詞法名稱,而是間接地經過對全局對象屬性的引用來對其進行訪問。
window.a經過這種技術能夠訪問那些被同名變量所遮蔽的全局變量。 但非全局的變量
若是被遮蔽了, 不管如何都沒法被訪問到。

不管函數在哪裏被調用,也不管它如何被調用,它的詞法做用域都只由函數被聲明時所處的位置決定。
詞法做用域查找只會查找一級標識符,好比a、b和c。若是代碼中引用了foo.bar.baz,詞法做用域查找只會試圖查找 foo 標識符,找到這個變量後, 對象屬性訪問規則會分別接管對 bar 和 baz 屬性的訪問

2.2. 欺騙詞法

JavaScript 中有兩個機制能夠「欺騙」詞法做用域:eval(..)和with。前者能夠對一段包
含一個或多個聲明的「代碼」字符串進行演算,並藉此來修改已經存在的詞法做用域(在
運行時)。後者本質上是經過將一個對象的引用看成做用域來處理,將對象的屬性看成做
用域中的標識符來處理,從而建立了一個新的詞法做用域(一樣是在運行時)。

3. 函數做用域和塊做用域

到底是什麼生成了一個新的氣泡?只有函數會生成新的氣泡嗎?JavaScript中的其餘結構能生成做用域氣泡嗎?

3.1. 隱藏內部實現

3.1.1. 最小受權|最小暴露原則

指在軟件設計中,應該最小限度地暴露必要內容,而將其餘內容都「隱藏」起來,好比某個模塊或對象的API設計。——可延伸到如何選擇做用域來包含變量和函數

如:

function doSomething(a) { b = a + doSomethingElse(a * 2); console.log(b * 3); } function doSomethingElse(a) { return a - 1; } var b; doSomething(2); // 15 /*在這個代碼片斷中, 變量 b 和函數 doSomethingElse(..) 應該是 doSomething(..) 內部具體實現的「私有」 內容。 給予外部做用域對 b 和 doSomethingElse(..) 的「訪問權限」 不只沒有必要且危險*/ // 更合理 function doSomething(a) { function doSomethingElse(a) { return a - 1; } var b; b = a + doSomethingElse(a * 2); console.log(b * 3); } d oSomething(2); // 15 //設計上將具體內容私有化
3.1.2. 規避衝突
  1. 全局命名空間
    用變量做爲庫的命名空間
    全部須要暴露給外界的功能都會成爲這個對象(命名空間)的屬性,而不是將本身的標識符暴漏在頂級的詞法做用域中

    如:

    var MyReallyCoolLibrary = { awesome: "stuff", doSomething: function() { // ... }, doAnotherThing: function() { // ...26 } };

3.2. 函數做用域

區分函數聲明和表達式最簡單的方法是看function關鍵字出如今聲明中的位置(不只僅是一行代碼,而是整個聲明中的位置)。若是function是聲明中的第一個詞,那麼就是一個函數聲明,不然就是一個函數表達式。

1. 匿名和具名

始終給函數表達式命名是一個最佳實踐

setTimeout( function timeoutHandler() { // 快看, 我有名字了 console.log( "I waited 1 second!" ); }, 1000 );

2. 當即執行函數表達式

/*第一種*/ var a = 2; (function foo() { var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 /* 第二種形式*/ (function foo(){ .. })() /*進階*/ /*將 window 對象的引用傳遞進去, 但將參數命名爲 global*/ var a = 2; (function IIFE( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 })( window ); console.log( a ); // 2 /*IIFE 還有一種變化的用途是倒置代碼的運行順序, 將須要運行的函數放在第二位, 在 IIFE執行以後看成參數傳遞進去。*/ var a = 2; (function IIFE( def ) { def( window ); })(function def( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 });

函數表達式 def 定義在片斷的第二部分, 而後看成參數(這個參數也叫做 def) 被傳遞進
IIFE 函數定義的第一部分中。 最後, 參數 def(也就是傳遞進去的函數) 被調用, 並將
window 傳入看成 global 參數的值。

函數不是惟一的做用域單元。塊做用域指的是變量和函數不只能夠屬於所處的做用域,也能夠屬於某個代碼塊(一般指 { .. } 內部)。

4. 變量提高

先有蛋(聲明) 後有雞(賦值)。

JavaScript 引擎將 var a和 a = 2 看成兩個單獨的聲明, 第一個是編譯階段的任務, 而第二個則是執行階段的任務。不管做用域中的聲明出如今什麼地方,都將在代碼自己被執行前首先進行處理。能夠將這個過程形象地想象成全部的聲明(變量和函數) 都會被「移動」 到各自做用域的最頂端, 這個過程被稱爲提高

只有聲明自己會被提高, 而賦值或其餘運行邏輯會留在原地。

4.1. 函數優先

函數聲明和變量聲明都會被提高。 可是一個值得注意的細節(這個細節能夠出如今有多個
「重複」 聲明的代碼中) 是 函數會首先被提高, 而後纔是變量。

5. 做用域閉包

閉包的建立和使用在你的代碼中隨處可見。你缺乏的是根據你本身的意願來識別、擁抱和影響閉包的思惟環境

5.1 什麼是閉包

當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前詞法做用域以外執行,這時就產生了閉包。

function foo() { var a = 2; function bar() { console.log(a); // 2 } bar(); } foo(); //基於詞法做用域的查找規則, 函數bar() 能夠訪問外部做用域中的變量 a function foo() { var a = 2; function bar() { console.log(a); } return bar; } var baz = foo(); baz(); // 2 —— 朋友, 這就是閉包的效果。

bar() 顯然能夠被正常執行。 可是在這個例子中, 它在本身定義的詞法做用域之外的地方執行
foo() 執行後垃圾回收器用來釋放再也不使用的內存空間,閉包的「神奇」之處正是能夠阻止這件事情的發生。 事實上內部做用域依然存在,bar() 依然持有對該做用域的引用, 而 這個引用就叫做閉包。
常見的閉包:

function wait(message) { setTimeout(function timer() { console.log(message); }, 1000); } wait("Hello, closure!"); //timer 具備涵蓋 wait(..) 做用域的閉包, 所以還保有對變量 message 的引用。 //wait(..) 執行 1000 毫秒後, 它的內部做用域並不會消失, timer 函數依然保有 wait(..)做用域的閉包。

只要使用了回調函數, 實際上就是在使用閉包!

5.2. 循環和閉包

for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); //6 }, i * 1000); } /*全部的回調函數依然是在循環結束後纔會被執行, 所以會每次輸出一個 6 出來。*/

緣由
缺陷是咱們試圖假設循環中的每一個迭代在運行時都會給本身「捕獲」 一個 i 的副本。
可是根據做用域的工做原理, 實際狀況是儘管循環中的五個函數是在各個迭代中分別定義的,
可是它們都被封閉在一個共享的全局做用域中, 所以實際上只有一個 i。
咱們須要更多的閉包做用域, 特別是在循環的過程當中每一個迭代都須要一個閉包做用域

//它須要有本身的變量, 用來在每一個迭代中儲存 i 的值: for (var i = 1; i <= 5; i++) { (function() { var j = i; setTimeout(function timer() { console.log(j); }, j * 1000); })(); } //使用let //本質上這是將一個塊轉換成一個能夠被關閉的做用域。 for (var i = 1; i <= 5; i++) { let j = i; // 是的, 閉包的塊做用域! setTimeout(function timer() { console.log(j); }, j * 1000); } //塊做用域和閉包聯手 for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000); }

5.3. 模塊

5.3.1. 模塊方式演進

模塊有兩個主要特徵:

  1. 爲建立內部做用域而調用了一個包裝函數;
  2. 包裝函數的返回值必須至少包括一個對內部函數的引用,這樣就會建立涵蓋整個包裝函數內部做用域的閉包。
    ```javascript
    //exa1:
    //兩個私有數據變量 something和 another, 以及 doSomething() 和 doAnother()
    //它們的詞法做用域(而這就是閉包) 也就是 foo() 的內部做用域。
    function foo() {
    var something = "cool";
    var another = [1, 2, 3];

    function doSomething() {
    console.log(something);
    }

    function doAnother() {
    console.log(another.join(" ! "));
    }
    }

//模塊
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() { console.log(something); } function doAnother() { console.log(another.join(" ! ")); } return { doSomething: doSomething, doAnother: doAnother };

}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
//1. CoolModule() 只是一個函數, 必需要經過調用它來建立一個模塊實例。 若是不執行外部函數, 內部做用域和閉包都沒法被建立。
//2. CoolModule() 返回一個用對象字面量語法 { key: value, ... } 來表示的對象。 這個返回的對象中含有對內部函數而不是內部數據變量的引用

//改進
var foo = (function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() { console.log(something); } function doAnother() { console.log(another.join(" ! ")); } return { doSomething: doSomething, doAnother: doAnother };

})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

//模塊模式另外一個簡單但強大的變化用法是, 命名將要做爲公共 API 返回的對象:
var foo = (function CoolModule(id) {
function change() {
// 修改公共 API
publicAPI.identify = identify2;
}

function identify1() { console.log(id); } function identify2() { console.log(id.toUpperCase()); } var publicAPI = { change: change, identify: identify1 }; return publicAPI;

})("foo module");
foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE
```
當經過返回一個含有屬性引用的對象的方式來將函數傳遞到詞法做用域外部時,咱們已經創造了能夠觀察和實踐閉包的條件。

所以 一個從函數調用所返回的,只有數據屬性而沒有閉包函數的對象並非真正的模塊

5.3.2. ES6的模塊

ES6 的模塊沒有「行內」 格式, 必須被定義在獨立的文件中(一個文件一個模塊),可
以在導入模塊時異步地加載模塊文件。

//bar.js function hello(who) { return "Let me introduce: " + who; } export hello //foo.js // 僅從 "bar" 模塊導入 hello() import hello from "bar"; var hungry = "hippo"; function awesome() { console.log( hello(hungry).toUpperCase() ); }

import能夠將一個模塊中的一個或多個API導入到當前做用域中,並分別綁定在一個變量上(在咱們的例子裏是hello)。module會將整個模塊的API導入並綁定到一個變量上(在咱們的例子裏是foo)。export會將當前模塊的一個標識符(變量、函數)導出爲公共API。這些操做能夠在模塊定義中根據須要使用任意屢次。

5.3.3. 動態做用域

動態做用域並不關心函數和做用域是如何聲明以及在何處聲明的,只關心它們從何處調用。換句話說,做用域鏈是基於調用棧的,而不是代碼中的做用域嵌套。

function foo() { console.log(a); //2 —— 若是是動態做用域3 } function bar() { var a = 3; foo(); } var a = 2; bar();

JavaScript並不具備動態做用域。它只有詞法做用域

主要區別:
詞法做用域是在寫代碼或者說定義時肯定的,而動態做用域是在運行時肯定的。(this也是!)詞法做用域關注函數在何處聲明,而動態做用域關注函數從何處調用

6. this詞法

6.1. _self
常見this綁定丟失解決方案: 'var _self = this'

6.2. 箭頭函數

ES6 中的箭頭函數引入了一個叫做 this 詞法的行爲

var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout(() => { // 箭頭函數是什麼鬼東西? this.count++; console.log("awesome?"); }, 100); } } }; obj.cool(); // 很酷吧 ?

簡單來講,箭頭函數在涉及this綁定時的行爲和普通函數的行爲徹底不一致。它放棄了全部普通this綁定的規則,取而代之的是用當前的詞法做用域覆蓋了this原本的值
這個代碼片斷中的箭頭函數只是「繼承」了cool()函數的this綁定(所以調用它並不會出錯)。

6.3. bind

//正確使用和包含 this 機制 var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout(function timer() { this.count++; // this 是安全的 // 由於 bind(..) console.log("more awesome"); }.bind(this), 100); // look, bind()! } } }; obj.cool(); // 更酷了。

 

 

 

 

var foo=function(){}
1 函數表達式?
2 也要有名字? var foo=function foooo(){}

爲何函數表達式有名字是最佳實踐?

 

匿名函數寫起來很方便,如今的不少庫使用的也不少,但有幾點風險:1. 若是一個函數被調用了屢次,出現問題後想要使用棧追蹤想看看都在哪裏調用了,若是是匿名函數的話不會顯示有意義的函數名稱,不方便調試2. 調用自身只能用arguments.callee了3. 解除事件綁定時,也須要函數名4. 影響可讀性

相關文章
相關標籤/搜索