1. 程式人生 > >重學前端

重學前端

led pan 布爾值 都得 原因 外部 pwa clu 消息隊列

一、前端基礎

JS:

1、Promise、Async有什麽區別

  1. Async 更簡潔,不需要用 then 連接
  2. Promise 中不能自定義使用 try/catch 進行錯誤捕獲,但是在 Async/await 中可以像處理同步代碼處理錯誤

  3. 調試更方便
  4. Generator 函數可以暫停執行和恢復執行,這是它能封裝異步任務的根本原因

2、介紹service worker

  1. 一個獨立的 worker 線程,獨立於當前網頁進程,有自己獨立的 worker context。

  2. 一旦被 install,就永遠存在,除非被 uninstall

  3. 需要的時候可以直接喚醒,不需要的時候自動睡眠(有效利用資源,此處有坑)

  4. 可編程攔截代理請求和返回,緩存文件,緩存的文件可以被網頁進程取到(包括網絡離線狀態)

  5. 能向客戶端推送消息

  6. 不能直接操作 DOM,但是service worker可以通過postMessage與頁面之間通信,把消息通知給頁面,如果需要的話,讓頁面自己去操作DOM。

  7. 出於安全的考慮,必須在 HTTPS 環境下才能工作

  8. 異步實現,內部大都是通過 Promise 實現

3、列舉Es6,常用的一些新特性

  • let, const:新增塊級作用域,分別定義變量和常量

  • class, extends, super:用class定義了一個“類”,可以看到裏面有一個constructor
    方法,這就是構造方法,而this關鍵字則代表實例對象;Class之間可以通過extends關鍵字實現繼承;super關鍵字,它指代父類的實例(即父類的this對象)30分鐘掌握ES6/ES2015核心內容(上)

  • 箭頭函數:函數體內的this對象,就是定義時所在的對象,而不是運行時時所在的對象。

  • 模板字符串(template string)

  • 解構(Destructuring)

  • ES6模塊:(import export):ES6模塊的設計思想,是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。CommonJS和AMD模塊,都只能在運行時確定這些東西。30分鐘掌握ES6/ES2015核心內容(下)

// 假設我們有兩個js文件: index.js和content.js,現在我們想要在index.js中使用content.js返回的結果,我們要怎麽做呢?

//content.js
define(‘content.js‘, function(){
    return ‘A cat‘;
})

// AMD
//index.js
require([‘./content.js‘], function(animal){
    console.log(animal);   //A cat
})

// CMD
//index.js
var animal = require(‘./content.js‘)

//content.js
module.exports = ‘A cat‘

// ES6
//index.js
import animal from ‘./content‘

//content.js
export default ‘A cat‘
  • API:實例感受-es6的常用語法和優越性

  keys:獲取對象鍵名,返回一個數組

  values:獲取對象值,返回一個數組

  entries:把對象鍵值對轉換成一個數組,返回一個包含鍵值對數組的新數組

  includes:用於某個數組或字符串是否包含給定的值,返回一個布爾值。如果沒找到符合條件的成員就返回 underfind

參考資料如下:
ECMAScript 6 入門
ES6 Promise 用法講解

4、說下自己對模塊化開發的理解,以及模塊化開發的好處

  • 提高開發效率,有利團隊協同開發,
  • 避免全局變量汙染,命名沖突,
  • 方便代碼的復用維護等。

5、瀏覽器事件流向

  1. “DOM2級事件”規定事件流包括三個階段,事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的事件捕獲,為截獲事件提供了機會,然後是實際的目標接收了事件,最後一個階段是冒泡階段
  2. 註意處於目標階段的時候,既不是冒泡階段、也不是捕獲階段,事件處理程序被調用的順序是註冊的順序

6、類型轉換規則

[] == ![]
  • 邏輯非運算符使 ![] 轉換為布爾值 false
  • 如果有一方是布爾值,就把布爾值轉換成 number 0 再比較 [] == 0
  • 如果一方是 object ,且另一方為 stringnumberobject就先調用 valueOf [].valueOf() 返回 [] 不是基本類型,再調用 toString 返回 ‘‘ ,最後將 ‘‘ 轉換為 0
  • 轉換順序:object / 布爾值 -> string -> number

7、如何實現深拷貝?

  • 很多人認為 Object.assign 是用來深拷貝的,其實並不是,Object.assign 只會拷貝所有的屬性值到新的對象中,如果屬性值是對象的話,拷貝的是地址,所以並不是深拷貝。
  • 展開運算符 ... 和 Object.assign() 行為一致, 執行的都是淺拷貝(只遍歷一層)
  • JSON.parse(JSON.stringify(object)) 可以用來深拷貝,但它會會忽略掉函數和 undefined
  • lodash 的深拷貝函數:
function deepClone(obj) {
  function isObject(o) {
    return (typeof o === ‘object‘ || typeof o === ‘function‘) && o !== null
  }

  if (!isObject(obj)) {
    throw new Error(‘非對象‘)
  }

  let isArray = Array.isArray(obj)
  // 擴展運算符只拷貝一層
  let newObj = isArray ? [...obj] : { ...obj }
  Reflect.ownKeys(newObj).forEach(key => {
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}

8、如何理解原型、原型鏈?

  • Object 是所有對象的爸爸,所有對象都可以通過 __proto__ 找到它
  • Function 是所有函數的爸爸,所有函數都可以通過 __proto__ 找到它
  • 函數的 prototype 是一個對象
  • 對象的 __proto__ 屬性指向原型, __proto__ 將對象和原型連接起來組成了原型鏈

9、基本類型判斷,如怎麽區分數組和對象?

  • 用typeof運算符來判斷

    技術分享圖片

  • 用Object的toString方法判斷 :兼容性最好的方法
  • 用Array對象的isArray方法判斷 :最便捷安全的方法
if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === ‘[object Array]‘;
  };
}

9、寫一個通用的事件偵聽器函數

      // 視能力分別使用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.removeEventListener) {
                 element.removeEventListener(type, handler, false);
             } else if (element.datachEvent) {
                 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;
         }

10、用js實現千位分隔符

/(\d)(?=(/d{3})+\.)/g

    num.toString()
          .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
              return $1 + ",";
          });

CSS:

1、介紹css3中position: sticky

  • absolute 生成絕對定位的元素,相對於值不為 static的第一個父元素進行定位。
  • fixed (老IE不支持) 生成絕對定位的元素,相對於瀏覽器窗口進行定位。
  • relative 生成相對定位的元素,相對於其正常位置進行定位。
  • static 默認值。沒有定位,元素出現在正常的流中(忽略 top, bottom, left, right z-index 聲明)。
  • inherit 規定從父元素繼承 position 屬性的值。
  • sticky 相對於最近的塊級父元素定位

  1. 父級元素不能有任何overflow:visible以為的overflow設置,否則沒有粘滯效果。因為改變了滾動容器(即使沒有出現滾動條)。因此,如果你的position:sticky無效,看看是不是某一個祖先元素設置了overflow:hidden,移除之即可。
  2. 同一個父容器中的sticky元素,如果定位值相等,則會重疊;如果屬於不同父元素,則會鳩占鵲巢,擠開原來的元素,形成依次占位的效果

2、瀏覽器的兼容性

  • png24位的圖片在iE6瀏覽器上出現背景,解決方案是做成PNG8
  • 瀏覽器默認的margin和padding不同。解決方案是加一個全局的*{margin:0;padding:0;}來統一
  • IE下,even對象有x,y屬性,但是沒有pageX,pageY屬性; Firefox下,event對象有pageX,pageY屬性,但是沒有x,y屬性
  • 一些新特性如 flex ,需要加前綴兼容不同的瀏覽器
  • even對象 Chrome 等瀏覽器用 e.target、而 IE 用 e.srcElement
  • 給元素添加監聽事件時,Chrome 等瀏覽器用的 addEventListener, 而IE 用 attachEvent

3、簡述一下你對HTML語義化的理解?

  • 用正確的標簽做正確的事情。
  • html語義化讓頁面的內容結構化,結構更清晰,便於對瀏覽器、搜索引擎解析;
  • 即使在沒有樣式CSS情況下也以一種文檔格式顯示,並且是容易閱讀的;
  • 搜索引擎的爬蟲也依賴於HTML標記來確定上下文和各個關鍵字的權重,利於SEO;
  • 便於閱讀維護理解。

4、請描述一下 cookies,sessionStorage 和 localStorage 的區別?

  • 是否攜帶:cookie是網站為了標示用戶身份而儲存在用戶本地終端(Client Side)上的數據(通常經過加密),會在 http 請求中攜帶(即使不需要)
  • 存儲大小: cookie數據大小不能超過4k。 sessionStorage和localStorage 雖然也有存儲大小的限制,但比cookie大得多,可以達到5M或更大。
  • 有期時間: localStorage 存儲持久數據,瀏覽器關閉後數據不丟失除非主動刪除數據; sessionStorage 數據在當前瀏覽器窗口關閉後自動刪除。 cookie 設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關閉

5、frame有那些缺點?

  • *iframe會阻塞主頁面的加載(Onload事件)
  • *不利於搜索引擎優化;

如果需要使用iframe,最好是通過javascript 動態給iframe添加src屬性值,這樣可以繞開以上兩個問題。

6、如何實現瀏覽器內多個標簽頁之間的通信? (阿裏)

  • WebSocket、SharedWorker;
  • 也可以調用localstorge、cookies等本地存儲方式;

localstorge另一個瀏覽上下文裏被添加、修改或刪除時,它都會觸發一個事件 storage

7、CSS3有哪些新特性?

  • 圓角 (border-radius:8px)
  • 陰影 (Shadow\Reflect)
  • 文字特效 (text-shadow、)
  • 線性漸變 (gradient)
  • 旋轉 (transform) 縮放,定位,動畫等,例如:transform:\scale(0.85,0.90)\ translate(0px,-30px)\ skew(-9deg,0deg)\Animation:

二、構建工具

1、Webpack熱更新實現原理

  1. 修改頁面代碼後,Webpack 監聽到文件修改後,開始編譯,編譯完成後,發送消息給客戶端
  2. 客戶端獲取到hash,成功後客戶端構造hot-update.js script鏈接,然後插入主文檔
  3. hot-update.js 插入成功後,執行hotAPI 的 createRecord 和 reload方法,獲取到 Vue 組件的 render方法,重新 render 組件, 繼而實現 UI 無刷新更新

三、框架原理

1、描述一下React 生命周期

  	渲染過程調用到的生命周期函數,主要幾個要知道;
  	* constructor?
  	* getInitialState?
  	* getDefaultProps?
  	* componentWillMount?
  	* render?
  	* componentDidMount?

  	更新過程

  	* componentWillReceiveProps?
  	* shouldComponentUpdate?
  	* componentWillUpdate?
  	* render?
  	* componentDidUpdate?

  	卸載過程

  	componentWillUnmount

2、實現組件有哪些方式?

  • React.createClass 使用API來定義組件 React ES6 class component 用 ES6 的class 來定義組件 Functional stateless component 通過函數定義無狀態組件

3、應該在React生命周期的什麽階段發出ajax請求,為什麽?

  • AJAX請求應在 componentDidMount函數 進行請求。因為此時組件已經掛載安裝,保證了數據更新後對應的組件也能更新

4、當組件的setState函數被調用之後,發生了什麽?

  • React 會將傳入的參數與組件當前的狀態合並,生成新的狀態 一> 根據新的狀態構建 React 元素樹 一> 計算出新的樹與老樹的節點差異,進行最小化重渲染

5、為什麽循環產生的組件中要利用上key這個特殊的prop?

  • Keys負責幫助React跟蹤列表中哪些元素被改變/添加/移除。React利用子元素的key在比較新舊元素樹的時候,快速得知一個元素是新的還是剛剛被移除。

6、介紹redux,主要解決什麽問題

  •   是管理應用程序狀態的庫,解決數據管理和數據通信的問題
  •   view 發出一個 action —> 派發器接收 action —> 讓 store 進行數據更新:reducer 進行 state 操作 —> view 通過 store 提供的 getState 獲取最新的數據  

7、redux 有什麽缺點

  • 一個組件所需要的數據,必須由父組件傳過來,而不能像 flux 中直接從 store 取。

  • 當一個組件相關數據更新時,即使父組件不需要用到這個組件,父組件還是會重新 render,可能會有效率影響,或者需要寫復雜的 shouldComponentUpdate 進行判斷。

8、為什麽虛擬 dom 會提高性能?(必考)

  • 虛擬 dom 相當於在 js 和真實 dom 中間加了一個緩存,利用 dom diff 算法避免了沒有必要的 dom 操作,從而提高性能。

9、react diff 原理(常考,大廠必考)

  • 把樹形結構按照層級分解,只比較同級元素。

  • 給列表結構的每個單元添加唯一的 key 屬性,方便比較。

  • 合並操作,調用 component 的 setState 方法的時候, React 將其標記為 dirty.到每一個事件循環結束, React 檢查所有標記 dirty 的 component 重新繪制.

  • 選擇性子樹渲染。開發人員可以重寫 shouldComponentUpdate 提高 diff 的性能

8、介紹react優化

  1. 用 shouldComponentUpdate 避免資源浪費
  2. 復雜的頁面不要在一個組件裏面寫完。
  3. 請盡量使用const element
  4. map裏面添加key,並且key不要使用index(可變的)。具體可參考使用Perf工具研究React Key對渲染的影響
  5. 盡量少用setTimeOut或不可控的refs、DOM操作。
  6. propsstate的數據盡可能簡單明了,扁平化。
  7. 使用return null而不是CSS的display:none來控制節點的顯示隱藏。保證同一時間頁面的DOM節點盡可能的少

四、性能優化

0、在開發項目上,知道那些優化的方式

  • 性能優化:減少 http 請求數、避免跳轉重定向、壓縮代碼圖片、減少DOM操作、避免全局變量、將樣式表放在頂部,將腳本放在底部、小圖片(<10kb)轉換base64編碼、資源按需加載等。
  • 代碼優化:有意義的命名,適當的註釋,代碼邏輯清晰,避免巨大函數,避免對象強耦合、使用事件代理、函數節流和防抖、禁止全局變量防止內存泄漏等。

1、函數節流和防抖

  • debounce:把觸發非常頻繁的事件(比如按鍵搜索)合並成一次執行。

  • throttle:保證每 X 毫秒恒定的執行次數,比如每200ms檢查下滾動位置,並觸發 CSS 動畫或請求數據。

  • requestAnimationFrame:可替代 throttle ,函數需要重新計算和渲染屏幕上的元素時,想保證動畫或變化的平滑性,可以用它。註意:IE9 不支持。

  requestAnimationFrame優點

  • 動畫保持 60fps(每一幀 16 ms),瀏覽器內部決定渲染的最佳時機
  • 簡潔標準的 API,後期維護成本低
  • 瀏覽器標簽未激活時,一切都不會執行

查看示例

2、介紹事件代理以及優缺點

在js中,當我們移除某個元素但沒有將元素和監聽函數進行解綁時,事件處理函數依舊會留在內存中,無法被當成垃圾回收。

  1. 減少事件註冊,節省內存
  2. 減少了dom節點更新的操作,處理邏輯只需在委托元素上進行
  3. 缺點:事件委托基於冒泡,對於onfoucs和onblur等事件不支持;層級過多,冒泡過程中,可能會被某層阻止掉

3、手寫原生js實現事件代理,並要求兼容瀏覽器

  • 其實就是考核對事件對象e的了解程度,以及在IE下對應的屬性名
  • addEventListener、attachEvent
  • e.target、e.srcElement
    function delegateEvent(interfaceEle, selector, type, fn) {
    
        if(interfaceEle.addEventListener){
        interfaceEle.addEventListener(type, eventfn);
        }else{
        interfaceEle.attachEvent("on"+type, eventfn);
        }
         
        function eventfn(e){
            var e = e || window.event;    
            var target = e.target || e.srcElement;
            //如果目標元素與選擇器匹配則執行函數
            if (matchSelector(target, selector)) {
                if(fn) {
                     //將fn內部的this指向target(在此之前this都是指向的綁定事件的元素即interfaceEle)
                    fn.call(target, e); 
                }
            }
        }
    }

4、React組件事件代理的原理、事件處理過程

  • 並非 #child 和 #parent 的事件分別代理到 document 上,而是 React 在 document 上綁定了一個 dispatchEvent 函數(事件),在執行 dispatchEvent 的過程中,其內部會依次執行 #child 和 #parent 上綁定的事件。
  • React 合成事件對象的e.stopPropagation,只能阻止 React 模擬的事件冒泡,並不能阻止真實的 DOM 事件冒泡,更加不能阻止已經觸發元素的多個事件的依次執行。在這種情況下,只有原生事件對象的 stopImmediatePropagation能做到。
  • 事件處理過程:技術分享圖片
  • 示例:技術分享圖片

5、什麽是內存泄漏?

本質上,內存泄漏可以定義為:應用程序不再需要占用內存的時候,由於某些原因,內存沒有被操作系統或可用內存池回收

所以,確保用完以後把它設置為 null 或者重新定義

  三種類型的常見 JavaScript 內存泄漏

  •   意外的全局變量
  •   被遺忘的計時器或回調函數
  •   脫離 DOM 的引用
  •   閉包裏多余的沒有被引用的變量
  • 實例:使用 Chrome 發現內存泄漏

6、PWA:Workbox

五、算法設計模式

前端架構主要解決的是高復用性,架構能力提升方向主要是組件庫開發、前端框架實現

1、觀察者和訂閱-發布的區別,各自用在哪裏

技術分享圖片技術分享圖片

  • 觀察者模式中,觀察者是知道Subject的,Subject一直保持對觀察者進行記錄。然而,在發布訂閱模式中,發布者和訂閱者不知道對方的存在。它們只有通過消息代理進行通信。

  • 發布訂閱模式中,組件是松散耦合的,正好和觀察者模式相反。

  • 觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去調用觀察者的方法。而發布-訂閱模式大多數時候是異步的(使用消息隊列)。

  • 觀察者設計模式定義了對象間的一種一對多的組合關系,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並自動刷新
  • 示例

六、網絡安全、通信協議

1、HTTP2.0和HTTP1.X相比的新特性

  1. 新的二進制格式,相對文本來說有更強的健壯性
  2. 多路復用(MultiPlexing),即連接共享。一個連接上可以有多個request,一個request對應一個id 。
  3. header壓縮:HTTP1.x的header帶有大量信息,而且每次都要重復發送,HTTP2.0使用encoder來減少需要傳輸的header大小,通訊雙方各自cache一份header fields表,既避免了重復header的傳輸,又減小了需要傳輸的大小。
  4. 服務端推送(server push)

2、通過什麽做到並發請求

對請求並發數進行限制,並且使用排隊機制讓請求有序的發送出去

3、http1.1時如何復用tcp連接

  使用 keep-alive

4、三次握手

  1. 客戶端向服務器發送一個 SYN(synchronize:同步) 包,請求建立連接
  2. 服務器收到後會發一個對SYN包的確認包ACK (Acknowledgement)即是確認字符回去
  3. 客戶端發送一個確認包(ACK),通知服務器連接已建立

5、什麽是 XSS 攻擊?如何防範 XSS 攻擊?什麽是 CSP?

Cross-Site Scripting(跨站腳本攻擊)簡稱 XSS,是一種代碼註入攻擊。攻擊者通過註入惡意腳本,使之在用戶的瀏覽器上運行

解決方法:

  • 轉義字符
  • CSP 本質上就是建立白名單,開發者明確告訴瀏覽器哪些外部資源可以加載和執行

什麽是 CSP(Content-Security-Policy) ?

內容安全策略 (CSP) 本質上就是建立白名單,開發者明確告訴瀏覽器哪些外部資源可以加載和執行

通常可以通過兩種方式來開啟 CSP:

  1. 設置 HTTP Header 中的 Content-Security-Policy
  2. 設置 meta 標簽的方式 <meta http-equiv="Content-Security-Policy">

示例:

  • 只允許加載本站資源

    Content-Security-Policy: default-src ‘self’
    
  • 只允許加載 HTTPS 協議圖片

    Content-Security-Policy: img-src https://*
  1. 【XSS】 美團

6、什麽是 CSRF 攻擊?如何防範 CSRF 攻擊?

CSRF(Cross-site request forgery)跨站請求偽造:冒充用戶執行某項操作的目的

  • CSRF自動防禦策略:同源檢測(Origin 和 Referer 驗證)。
  • CSRF主動防禦措施:Token驗證 或者 雙重Cookie驗證 以及配合Samesite Cookie。
  • 保證頁面的冪等性,後端接口不要在GET頁面中做用戶操作。

示例:

2007年Gmail的CSRF漏洞:點開一個黑客的鏈接,所有郵件都被竊取(攻擊者誘導用戶進入某個頁面,在頁面中通過表單提交 POST 請求,也說明了表單是可以進行跨域攻擊的)

2008年YouTube上幾乎所有用戶可以操作的動作都存在CSRF漏洞

2012年WordPress發現了一個操作用戶賬戶的CSRF漏洞:攻擊者引導用戶先進入目標的WordPress,然後點擊其釣魚站點上的某個按鈕,該按鈕實際上是表單提交按鈕,會添加某個具有管理員權限的用戶

7、什麽是點擊劫持?如何防範點擊劫持?

點擊劫持是一種視覺欺騙的攻擊手段。攻擊者將需要攻擊的網站通過 iframe 嵌套的方式嵌入自己的網頁中,並將 iframe 設置為透明,在頁面中透出一個按鈕誘導用戶點擊。

X-FRAME-OPTIONS 是一個 HTTP 響應頭,在現代瀏覽器有一個很好的支持。這個 HTTP 響應頭 就是為了防禦用 iframe 嵌套的點擊劫持攻擊。

  • DENY,表示頁面不允許通過 iframe 的方式展示
  • SAMEORIGIN,表示頁面可以在相同域名下通過 iframe 的方式展示
  • ALLOW-FROM,表示頁面可以在指定來源的 iframe 中展示

8、什麽是中間人攻擊?如何防範中間人攻擊?

中間人攻擊是攻擊方同時與服務端和客戶端建立起了連接,並讓對方認為連接是安全的,但是實際上整個通信過程都被攻擊者控制了。攻擊者不僅能獲得雙方的通信信息,還能修改通信信息。如:不安全的公共 WIFI

9、URI 與 URL 的區別

  • URI:統一資源標誌符
  • URL:統一資源定位符
  • URN:URN 是另一種形式的 URI,它通過特定命名空間中的唯一名稱來標識資源,比如 ISBN 版本號,urn:isbn:9780141036144
  • URI 包括很多協議,如 HTTPS、data、URN 等等,所以 URL 和 URN 都是 URI 的子集

重學前端