前端知識點彙總—面試看這一篇就夠了

一年前寫了一篇JavaScript八張思惟導圖,主要是對前端JavaScript知識點的一個系統的整理和總結。本篇文章用了近一個月時間,蒐集整理了網上各類面試筆試題及本人針對前端的一些理解以及各路大神針對前端難點部分的詳細介紹,能夠做爲之後面試或者考察面試人員的參考。
相信經過這兩篇文章的學習,必定會讓你對前端有一個更深的認識。javascript

後續會持續更新......css

JavaScript基礎

介紹JS的基本數據類型

string number boolean undefined nullhtml

介紹JS有哪些內置對象

  • 數據封裝類對象: String Number Boolean Array Object
  • 其餘對象: Function Math Date Error Arguments RegExp

宿主對象和原生對象的區別

  • 原生對象(native object)是由ECMAScript規範定義的JavaScript內置對象,如 String Number Boolean Array Object Function Math Date Error Arguments RegExp等。
  • 宿主對象(host object)是由運行時的環境(瀏覽器或node)決定的,如window、XMLHTTPRequest等。

null、undefined及未聲明變量之間的區別。如何區分?

  • 未聲明變量默認值爲undefined
  • typeof null === 'object' // true
  • typeof undefined === 'undefined' // true

==和===的區別

  • ==比較以前會先進行類型轉換,即不會對類型進行比較。例如:前端

    12 == '12'  // true
      true == 1   // true
      false == '0' // true
  • ===會比較數值和類型。例如:vue

    12 === '12' // false
      12 === 12 // true
      true === 1 // false
      false === '0' // false

JS隱式轉換及應用場景

JS在使用運算符號或者對比符時,會自帶隱式轉換,規則以下:java

  • -、*、/、% :一概轉換成數值後計算
  • +:node

    • 數字 + 字符串 = 字符串, 運算順序是從左到右
    • 數字 + 對象, 優先調用對象的valueOf -> toString
    • 數字 + boolean/null -> 數字
    • 數字 + undefined -> NaN
  • [1].toString() === '1'
  • {}.toString() === '[object object]'
  • NaN !== NaN 、+undefined 爲 NaNjquery

  • http://www.javashuo.com/article/p-chxwgmee-y.htmlnginx

"Attribute"和"Property"的區別

"Attribute"是在HTML中定義的,而"property"是在DOM上定義的。爲了說明區別,假設咱們在HTML中有一個文本框:
<input type="text" value="Hello">git

const input = document.querySelector('input');
console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello

可是在文本框中鍵入「 World!」後:

console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello World!

NaN是什麼?如何判斷是不是NaN類型

  • 定義: 全局屬性 NaN 的值表示不是一個數字(Not-A-Number)
  • 如何判斷一個值是不是NaN: 等號運算符(== 和 ===) 不能被用來判斷一個值是不是 NaN。必須使用 Number.isNaN() 或 isNaN() 函數。

    NaN === NaN;        // false
      Number.NaN === NaN; // false
      isNaN(NaN);         // true
      isNaN(Number.NaN);  // true

如何判斷兩個對象相等

須要考慮三個問題:

  1. 若是對象的屬性值之一自己就是一個對象
  2. 若是屬性值中的一個是NaN(在JavaScript中,是否是等於本身惟一的價值?)
  3. 若是一個屬性的值爲undefined,而另外一個對象沒有這個屬性(於是計算結果爲不肯定?)

檢查對象的「值相等」的一個強大的方法,最好是依靠完善的測試庫,涵蓋了各類邊界狀況。Underscore和Lo-Dash有一個名爲_.isEqual()方法,用來比較好的處理深度對象的比較。您可使用它們像這樣:

// Outputs: true
console.log(_.isEqual(obj1, obj2));

什麼是'user strict',使用它有什麼優缺點?

'use strict' 是用於對整個腳本或單個函數啓用嚴格模式的語句。嚴格模式是可選擇的一個限制 JavaScript 的變體一種方式 。
優勢:

  • 沒法再意外建立全局變量。
  • 會使引發靜默失敗(silently fail,即:不報錯也沒有任何效果)的賦值操拋出異常。
  • 試圖刪除不可刪除的屬性時會拋出異常(以前這種操做不會產生任何效果)。
  • 要求函數的參數名惟一。
  • 全局做用域下,this的值爲undefined。
  • 捕獲了一些常見的編碼錯誤,並拋出異常。
  • 禁用使人困惑或欠佳的功能。

缺點:

  • 缺失許多開發人員已經習慣的功能。
  • 沒法訪問function.caller和function.arguments。
  • 以不一樣嚴格模式編寫的腳本合併後可能致使問題。

總的來講,我認爲利大於弊,我歷來不使用嚴格模式禁用的功能,所以我推薦使用嚴格模式。

call,apply和bind的做用是什麼?二者區別是什麼?

.call和.apply都用於調用函數,第一個參數將用做函數內 this 的值。然而,.call接受逗號分隔的參數做爲後面的參數,而.apply接受一個參數數組做爲後面的參數。一個簡單的記憶方法是,從call中的 C 聯想到逗號分隔(comma-separated),從apply中的 A 聯想到數組(array)。

function add(a, b) {
return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3

.call和.apply是當即執行的, .bind 返回函數的副本,但帶有綁定上下文!它不是當即執行的。

const person = { name: 'Lydia' }

function sayHi(age) {
console.log(`${this.name} is ${age}`)
}

sayHi.call(person, 21)
sayHi.bind(person, 21)

結果: Lydia is 21 function

請說明Function.prototype.bind的用法

摘自MDN:

bind()方法建立一個新的函數, 當被調用時,將其 this 關鍵字設置爲提供的值,在調用新函數時,在任何提供以前提供一個給定的參數序列。

根據個人經驗,將this的值綁定到想要傳遞給其餘函數的類的方法中是很是有用的。在 React 組件中常常這樣作。

如何判斷是否爲空數組

var arr = [];
if (Array.isArray(arr) && arr.length === 0) {
    console.log('是空數組');
}
// Array.isArray是ES5提供的,若是不支持。用下面的方案。
if (!Array.isArray) {
    Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
}

數組方法

  • map: 遍歷數組,返回回調返回值組成的新數組
  • forEach: 沒法break,能夠用try/catch中throw new Error來中止
  • filter: 過濾
  • some: 有一項返回true,則總體爲true
  • every: 有一項返回false,則總體爲false
  • join: 經過指定鏈接符生成字符串
  • sort(fn) / reverse: 排序與反轉,改變原數組
  • concat: 鏈接數組,不影響原數組, 淺拷貝
  • slice(start, end): 返回截斷後的新數組,不改變原數組
  • splice(start, number, value...): 返回刪除元素組成的數組,value 爲插入項,改變原數組
  • indexOf / lastIndexOf(value, fromIndex): 查找數組項,返回對應的下標
  • reduce / reduceRight(fn(prev, cur), defaultPrev): 兩兩執行,prev 爲上次化簡函數的return值,cur 爲當前值(從第二項開始)

數組亂序:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});

數組拆解:

// flat: [1,[2,3]] --> [1, 2, 3]

Array.prototype.flat = function() {
    return this.toString().split(',').map(item => +item )
}

push、pop、shift、unshift功能及返回值

  • push:添加新元素到數組末尾,返回數組長度
  • pop:移除數組最後一個元素,返回元素自己
  • unshift:添加新元素到數組開頭,返回數組長度
  • shift:移除數組首個元素,返回元素自己

以上4種操做均會改變數組自己

.forEach和.map()循環的主要區別,使用場景舉例

map用法:

let array = [1, 2, 3, 4, 5];
let newArray = array.map((item, i, arr) => {
    return item * 2;
});
console.log("array:", array);       // [1, 2, 3, 4, 5]
console.log("newArray:", newArray); // [2, 4, 6, 8, 10]
// 此處的array接受map方法運算以後的返回值
// 可是map方法並不能改變原來的數組

forEach用法:

let array = [1, 2, 3, 4, 5];
let newArray = array.forEach((item, i, arr) => {
    console.log('item:' + item + ', index:' + i);
    // array[i] = item * 2;    // 能夠用這種方式改變原始數組的值
});
console.log("array:", array);       // [1, 2, 3, 4, 5]
console.log("newArray:", newArray); // undefined
// forEach方法沒有返回值
  • 能用forEach()作到的,map()一樣能夠。反過來也是如此。
  • map()會分配內存空間存儲新數組並返回,forEach()不會返回數據。
  • forEach()容許callback更改原始數組的元素。map()返回新的數組。

JS執行對象查找時,永遠不會去查找原型的函數是哪一個?

Object​.prototype​.has​OwnProperty()

如何將arguments轉爲數組

  1. Array.prototype.slice.call(arguments)
  2. [].slice.call(arguments)

對象的遍歷方法

  • for循環

    for (let property in obj) {
          console.log(property);
      }

    可是,這還會遍歷到它的繼承屬性,在使用以前,你須要加入obj.hasOwnProperty(property)檢查。

  • Object.keys()

    Object.keys(obj).forEach((property) => { ... })

    Object.keys()方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組。

  • Object.getOwnPropertyNames()

    Object.getOwnPropertyNames(obj).forEach((property) => { ... })

    Object.getOwnPropertyNames()方法返回一個由指定對象的全部自身屬性的屬性名(包括不可枚舉屬性但不包括 Symbol 值做爲名稱的屬性)組成的數組。

數組的遍歷方法

  • for loops

    for (let i = 0; i < arr.length; i++) { ... }
  • forEach

    arr.forEach((item, index, arr) { ... })
  • map

    arr.map((item, index, arr) => { ... })

匿名函數的典型應用場景

匿名函數能夠在 IIFE 中使用,來封裝局部做用域內的代碼,以便其聲明的變量不會暴露到全局做用域。

(function() {
    // 一些代碼。
})();

匿名函數能夠做爲只用一次,不須要在其餘地方使用的回調函數。當處理函數在調用它們的程序內部被定義時,代碼具備更好地自閉性和可讀性,能夠省去尋找該處理函數的函數體位置的麻煩。

setTimeout(function() {
    console.log('Hello world!');
}, 1000);

匿名函數能夠用於函數式編程或 Lodash(相似於回調函數)。

const arr = [1, 2, 3];
const double = arr.map(function(el) {
    return el * 2;
});
console.log(double); // [2, 4, 6]

IIFE(當即執行函數)的用法

IIFE( 當即調用函數表達式)是一個在定義時就會當即執行的 JavaScript 函數。

(function () {
    statements
})();

這是一個被稱爲 自執行匿名函數 的設計模式,主要包含兩部分。第一部分是包圍在 圓括號運算符 () 裏的一個匿名函數,這個匿名函數擁有獨立的詞法做用域。這不只避免了外界訪問此 IIFE 中的變量,並且又不會污染全局做用域。

第二部分再一次使用 () 建立了一個當即執行函數表達式,JavaScript 引擎到此將直接執行函數。

document的load事件和DOMContentLoaded事件之間的區別

當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。

window的load事件僅在 DOM 和全部相關資源所有完成加載後纔會觸發。

數據類型判斷方式有幾種

下面將對以下數據進行判斷它們的類型

var bool = true
var num = 1
var str = 'abc'
var und = undefined
var nul = null
var arr = [1, 2, 3]
var obj = {a: 'aa', b: 'bb'}
var fun = function() {console.log('I am a function')}
  • typeof: 適合基本的數據類型和函數

    console.log(typeof bool);   // boolean
      console.log(typeof num);    // number
      console.log(typeof str);    // string
      console.log(typeof und);    // undefined
      console.log(typeof nul);    // object
      console.log(typeof arr);    // object
      console.log(typeof obj);    // object
      console.log(typeof fun);    // function

    由結果可知typeof能夠測試出number、string、boolean、undefined及function,而對於null及數組、對象,typeof均檢測出爲object,不能進一步判斷它們的類型。

  • instanceof: 判斷對象類型,基於原型鏈去判斷。
    obj instanceof Object: 左操做數是一個對象,右操做數是一個函數構造器或者函數對象,判斷左邊的操做數的原型鏈_proto_屬性是否有右邊這個函數對象的proptotype屬性。

    console.log(bool instanceof Boolean);// false
      console.log(num instanceof Number); // false
      console.log(str instanceof String); // false
      console.log(und instanceof Object); // false
      console.log(arr instanceof Array);  // true
      console.log(nul instanceof Object); // false
      console.log(obj instanceof Object); // true
      console.log(fun instanceof Function);// true
    
      var bool2 = new Boolean()
      console.log(bool2 instanceof Boolean);// true
    
      var num2 = new Number()
      console.log(num2 instanceof Number);// true
    
      var str2 = new String()
      console.log(str2 instanceof String);//  true
    
      function Animation(){}
      var ani = new Animation()
      console.log(ani instanceof Animation);// true
    
      function Dog(){}
      Dog.prototype = new Animation()
      var dog = new Dog()
      console.log(dog instanceof Dog);    // true
      console.log(dog instanceof Animation);// true
      console.log(dog instanceof Object); // true

    從結果中看出instanceof不能區別undefined和null,並且對於基本類型若是不是用new聲明的則也測試不出來,對因而使用new聲明的類型,它還能夠檢測出多層繼承關係。

  • constructor: 返回對建立此對象的函數的引用

    console.log(bool.constructor === Boolean);  // true
      console.log(num.constructor === Number);    // true
      console.log(str.constructor === String);    // true
      console.log(arr.constructor === Array);     // true
      console.log(obj.constructor === Object);    // true
      console.log(fun.constructor === Function);  // true
    
      console.log(ani.constructor === Animation); // true
      console.log(dog.constructor === Dog);       // false
      console.log(dog.constructor ===  Animation);// true

    null 和 undefined 是無效的對象,所以是不會有 constructor 存在的,這兩種類型的數據須要經過其餘方式來判斷。

    函數的 constructor 是不穩定的,這個主要體如今自定義對象上,當開發者重寫 prototype 後,原有的 constructor 引用會丟失。因此dog.constructor === Animation 而不是 Dog

  • Object.prototype.toString.call

    console.log(Object.prototype.toString.call(bool));  //[object Boolean]
      console.log(Object.prototype.toString.call(num));   //[object Number]
      console.log(Object.prototype.toString.call(str));   //[object String]
      console.log(Object.prototype.toString.call(und));   //[object Undefined]
      console.log(Object.prototype.toString.call(nul));   //[object Null]
      console.log(Object.prototype.toString.call(arr));   //[object Array]
      console.log(Object.prototype.toString.call(obj));   //[object Object]
      console.log(Object.prototype.toString.call(fun));   //[object Function]
    
      console.log(Object.prototype.toString.call(dog));   //[object Object]

    原理(摘自高級程序設計3):在任何值上調用 Object 原生的 toString() 方法,都會返回一個 [object NativeConstructorName] 格式的字符串。每一個類在內部都有一個 [[Class]] 屬性,這個屬性中就指定了上述字符串中的構造函數名。
    可是它不能檢測非原生構造函數的構造函數名。

  • 使用jquery中的$.type

    console.log($.type(bool));  //boolean
      console.log($.type(num));   //number
      console.log($.type(str));   //string
      console.log($.type(und));   //undefined
      console.log($.type(nul));   //null
      console.log($.type(arr));   //array
      console.log($.type(obj));   //object
      console.log($.type(fun));   //function
    
      console.log($.type(dog));   //object

    $.type()內部原理就是用的Object.prototype.toString.call()

DOM操做(增刪改查)

  • 添加操做

    let element = document.createElement("div");   // 建立元素
      body.appendChild(element);  // 將一個節點添加到指定父節點的子節點列表末尾
  • 刪除操做

    var oldChild = node.removeChild(child); // 刪除子元素
      ChildNode.remove() // 刪除元素
  • 修改操做

    Node.innerText // 修改元素文本內容
      Element.innerHTML // 設置或獲取描述元素後代的HTML語句
  • 查找操做

    Document.getElementById() // 返回對擁有指定 id 的第一個對象的引用
      Document.querySelector() // 返回文檔中匹配指定的CSS選擇器的第一元素
      Document.querySelectorAll() // 返回與指定的選擇器組匹配的文檔中的元素列表
  • https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model

異步加載JS 的方式有哪些

瀏覽器下載除JS外的資源時,會並行下載,以提升性能。但下載JS腳本時,會禁止並行下載(稱爲腳本阻塞Scripts Block Downloads)。瀏覽器遇到JS時,必須等JS下載,解析,執行完後,才能繼續並行下載下一個資源。緣由是JS可能會改變頁面或改變JS間的依賴關係,例如A.js中用document.write改變頁面,B.js依賴於A.js。所以要嚴格保證順序,不能並行下載。

因爲瀏覽器在遇到<body>標籤前是不會渲染頁面的,爲了不白屏,一般的建議是將JS放到標籤底下,能夠有最佳的用戶體驗。

按推薦度排序:

  1. 動態建立<script>標籤(Dynamic Script Element)

    var script = document.createElement('script');  // 建立script標籤
     script.type = "text/javascript";
     script.src = "A.js";
     document.getElementsByTagName('head')[0].appendChild(script);   // 塞進頁面

    先用document.createElement(‘script’)生成一個script標籤,再設置它的src屬性,最後將其插入到<head>中。

    script標籤被插入到頁面的DOM樹後,就會開始下載src屬性指定的腳本。並且經過動態腳本元素下載腳本是異步的,不會阻塞頁面的其餘下載和處理過程,所以script標籤插入<head>中也沒問題。

  2. Script async

    <script type="text/javascript" src="A.js" async></script>

    瀏覽器解析到HTML裏的該行script標籤,發現指定爲async,會異步下載解析執行腳本。

    async 是HTML5裏爲script標籤新增的屬性,對於低版本瀏覽器會存在兼容性問題。

    它會在下載完成後馬上執行,而不是會等到DOM加載完成以後再執行,因此仍是有可能會形成阻塞。

    這種方式只適用於引用外部js文件的<script>標籤。

    添加async屬性的js文件不該該使用document.write方法。

    對多個帶有async的js文件,它不能保證按順序執行,它是哪一個js文件先下載完就先執行哪一個。

  3. Script defer

    <script type="text/javascript" src="A.js" defer></script>

    瀏覽器解析到HTML裏的該行script標籤,發現指定爲defer,會暫緩下載解析執行腳本。而是等到頁面加載完畢後,才加載腳本(更精確地說,是在DOM樹構建完成後,在window.onload觸發前,加載defer的腳本)。

    defer也是隻適用於外部js文件,也不能在js中使用document.write方法。

    能夠保證多個js文件的執行順序就是它們在頁面中出現的順序。

document.write和innerHTML有何區別

  • document.write會重繪整個頁面,若是不指定元素的話,它會覆蓋掉整個頁面內容。
  • innerHTML只會重繪頁面的一部分。

jQuery.extend和jQuery.fn.extend的區別

針對jQuery性能的優化方法

如何判斷當前腳本運行在瀏覽器仍是node環境中

經過判斷Global對象是否爲window,若是不爲window,則當前腳本運行在node.js環境中。

this === window ? 'browser' : 'node';

Canvas和SVG的比較

Ajax的原理

JS事件委託、事件冒泡

IE和Firefox的事件機制有何區別,如何阻止冒泡?

  • IE只支持事件冒泡,火狐同時支持事件捕獲事件冒泡兩種。
  • 阻止事件冒泡的方式不一樣
    IE: e.cancelBubble = true
    W3C: e.stopPropagation()

JS內存空間的管理

  • 基礎數據類型與棧內存
    JS中的基礎數據類型,這些值都有固定的大小,每每都保存在棧內存中,由系統自動分配存儲空間。咱們能夠直接操做保存在棧內存空間的值,所以基礎數據類型都是按值訪問。

    數據在棧內存中的存儲與使用方式相似於數據結構中的堆棧數據結構,遵循後進先出的原則。

    基礎數據類型: Number String Null Undefined Boolean

  • 引用數據類型與堆內存
    與其餘語言不一樣,JS的引用數據類型,好比數組Array、對象Object、函數Function,它們值的大小是不固定的。引用數據類型的值是保存在堆內存中的對象。JavaScript不容許直接訪問堆內存中的位置,所以咱們不能直接操做對象的堆內存空間。

在操做對象時,其實是在操做對象的引用而不是實際的對象。所以,引用類型的值都是按引用訪問的。這裏的引用,咱們能夠粗淺地理解爲保存在棧內存中的一個地址,該地址與堆內存的實際值相關聯。

總結:

棧內存 堆內存
存儲基礎數據類型 存儲引用數據類型
按值訪問 按引用訪問
存儲的值大小固定 存儲的值大小不定,可動態調整
由系統自動分配內存空間 由開發人員經過代碼分配
主要用來執行程序 主要用來存放對象
空間小,運行效率高 空間大,可是運行效率相對較低
先進後出,後進先出 無序存儲,可根據引用直接獲取

JS執行上下文

JS變量對象詳解

請解釋變量提高

JS做用域及做用域鏈/閉包(closure),經常使用場景舉例說明

請簡述JS中的this

JS函數與函數式編程

JS原型,原型鏈。實現繼承的方式

JS有哪幾種建立對象的方式

  1. 利用Object構造函數方式建立
  2. 利用對象字面量方式建立
  3. 利用工廠模式建立
  4. 利用構造函數模式建立
  5. 利用原型方式建立
  6. 利用構造函數和原型的組合模式建立

請解釋事件循環,調用堆棧和任務隊列的區別

談談對Promise的理解

ES6知識點

  1. var let const
  2. =>箭頭函數
  3. 模版字符串
  4. 解析結構
  5. 函數默認參數
  6. ...展開運算符
  7. class
  8. Promise

防抖與節流

防抖與節流函數是一種最經常使用的 高頻觸發優化方式,能對性能有較大的幫助。

  • 防抖 (debounce): 將屢次高頻操做優化爲只在最後一次執行,一般使用的場景是:用戶輸入,只需再輸入完成後作一次輸入校驗便可。

  • 節流(throttle): 每隔一段時間後執行一次,也就是下降頻率,將高頻操做優化成低頻操做,一般使用場景: 滾動條事件 或者 resize 事件,一般每隔 100~500 ms執行一次便可。

    function throttle(method, context) {
          clearTimeout(method.tID);
          method.tID = setTimeout(function () {
              method.call(context);
          }, 1000);
      }

    用法:

    function showTime() {
          console.log("nowDate:" + new Date().toLocaleDateString());
      }
    
      setInterval(function () {
          throttle(showTime);
      }, 2000);

模塊化

模塊化開發在現代開發中已經是必不可少的一部分,它大大提升了項目的可維護、可拓展和可協做性。一般,咱們 在瀏覽器中使用 ES6 的模塊化支持,在 Node 中使用 commonjs 的模塊化支持。

分類:

  • es6: import / export
  • commonjs: require / module.exports / exports
  • amd: require / defined

require與import的區別

  • require支持 動態導入,import不支持,正在提案 (babel 下可支持)
  • require是 同步 導入,import屬於 異步 導入
  • require是 值拷貝,導出值變化不會影響導入值;import指向 內存地址,導入值會隨導出值而變化

oAuth實現方案

如何實現單點登陸(Single Sign On)

請解釋SPA(單頁應用),優缺點是什麼?如何使其對SEO友好

單頁Web應用(single page web application,SPA),就是隻有一張Web頁面的應用,是加載單個HTML 頁面並在用戶與應用程序交互時動態更新該頁面的Web應用程序。

單頁應用SPA 多頁應用MPA
組成 一個外殼頁面和多個頁面片斷組成 多個完整頁面構成
資源共用(css,js) 共用,只需在外殼部分加載 不共用,每一個頁面都須要加載
刷新方式 頁面局部刷新或更改 整頁刷新
url 模式 a.com/#/pageone
a.com/#/pagetwo
a.com/pageone.html
a.com/pagetwo.html
用戶體驗 頁面片斷間的切換快,用戶體驗良好
因爲要一次加載全部的資源(html/js),故首屏加載慢
頁面切換加載緩慢,流暢度不夠,用戶體驗比較差
首屏加載很快
轉場動畫 容易實現 沒法實現
數據傳遞 容易 依賴 url傳參、或者cookie 、localStorage等
搜索引擎優化(SEO) 須要單獨方案、實現較爲困難、不利於SEO檢索。
Prerender預渲染優化SEO
實現方法簡易
試用範圍 高要求的體驗度、追求界面流暢的應用 適用於追求高度支持搜索引擎的應用
開發成本 較高,常需藉助專業的框架 較低,但頁面重複代碼多
維護成本 相對容易 相對複雜

前端性能優化方案

如下是移動端的優化方案,大部分Web端也一樣適用

正則表達式

設計模式舉例(實現、應用、優缺點)

前端經常使用框架對比

JS編碼規範

瀏覽器相關

瀏覽器架構

  • 用戶界面
  • 主進程
  • 內核
    • 渲染引擎
    • JS 引擎
      • 執行棧
    • 事件觸發線程
      • 消息隊列
        • 微任務
        • 宏任務
    • 網絡異步線程

瀏覽器下事件循環(Event Loop)

事件循環是指: 執行一個宏任務,而後執行清空微任務列表,循環再執行宏任務,再清微任務列表

  • 微任務 microtask(jobs): promise / ajax / Object.observe(該方法已廢棄)
  • 宏任務 macrotask(task): setTimout / setInterval / script / IO / UI Rendering

瀏覽器解析流程

從輸入 url 到展現的過程

  • DNS 解析
  • TCP 三次握手
  • 發送請求,分析 url,設置請求報文(頭,主體)
  • 服務器返回請求的文件 (html)
  • 瀏覽器渲染
    • HTML parser --> DOM Tree
      • 標記化算法,進行元素狀態的標記
      • dom 樹構建
    • CSS parser --> Style Tree
      • 解析 css 代碼,生成樣式樹
    • attachment --> Render Tree
      • 結合 dom樹 與 style樹,生成渲染樹
    • layout: 佈局
    • GPU painting: 像素繪製頁面

功能檢測、功能推斷、navigator.userAgent的區別

功能檢測(feature detection)

功能檢測包括肯定瀏覽器是否支持某段代碼,以及是否運行不一樣的代碼(取決於它是否執行),以便瀏覽器始終可以正常運行代碼功能,而不會在某些瀏覽器中出現崩潰和錯誤。例如:

if ('geolocation' in navigator) {
// 可使用 navigator.geolocation
} else {
// 處理 navigator.geolocation 功能缺失
}

Modernizr是處理功能檢測的優秀工具。

功能推斷(feature inference)

功能推斷與功能檢測同樣,會對功能可用性進行檢查,可是在判斷經過後,還會使用其餘功能,由於它假設其餘功能也可用,例如:

if (document.getElementsByTagName) {
element = document.getElementById(id);
}

很是不推薦這種方式。功能檢測更能保證萬無一失。

UA 字符串

這是一個瀏覽器報告的字符串,它容許網絡協議對等方(network protocol peers)識別請求用戶代理的應用類型、操做系統、應用供應商和應用版本。它能夠經過navigator.userAgent訪問。 然而,這個字符串很難解析而且極可能存在欺騙性。例如,Chrome 會同時做爲 Chrome 和 Safari 進行報告。所以,要檢測 Safari,除了檢查 Safari 字符串,還要檢查是否存在 Chrome 字符串。不要使用這種方式。
考慮到歷史緣由及現代瀏覽器中用戶代理字符串(userAgent)的使用方式,經過用戶代理字符串來檢測特定的瀏覽器並非一件輕鬆的事情。因此使用用戶代理檢測是最後的選擇。
用戶代理檢測通常適用如下的情形:

  • 不能直接準確的使用功能檢測。
  • 同一款瀏覽器在不一樣平臺下具有不一樣的能力。這個時候可能就有必要肯定瀏覽器位於哪一個平臺。
  • 爲了跟蹤分析等目的須要知道特定的瀏覽器。

瀏覽器版本檢測方式

可使用navigator.userAgent。

JS同源策略(same-origin policy)

同源策略限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。

下表給出了相對http://store.company.com/dir/page.html同源檢測的示例:

URL 結果 緣由
http://store.company.com/dir2/other.html 成功 只有路徑不一樣
http://store.company.com/dir/inner/another.html 成功 只有路徑不一樣
https://store.company.com/secure.html 失敗 不一樣協議 ( https和http )
http://store.company.com:81/dir/etc.html 失敗 不一樣端口 ( http:// 80是默認的)
http://news.company.com/dir/other.html 失敗 不一樣域名 ( news和store )

跨標籤頁通信

不一樣標籤頁間的通信,本質原理就是去運用一些能夠 共享的中間介質,所以比較經常使用的有如下方法:

  • 經過父頁面window.open()和子頁面postMessage
    • 異步下,經過 window.open('about: blank') 和 tab.location.href = '*'
  • 設置同域下共享的localStorage與監聽window.onstorage
    • 重複寫入相同的值沒法觸發
    • 會受到瀏覽器隱身模式等的限制
  • 設置共享cookie與不斷輪詢髒檢查(setInterval)
  • 藉助服務端或者中間層實現

跨域的解決方案

按實際使用量排序(我的理解):

瀏覽器數據本地存儲方法(localStroage、sessionStroage、cookie、indexedDB)

目前常見的存儲方式爲如下三種:

  • Cookie
  • web存儲 (locaStorage和seesionStorage)
  • IndexedDB

在H5出現以前,數據都是存儲在cookie中的。爲了解決cookie的侷限性引入了Web存儲,indexedDB用於客戶端存儲大量結構化數據(包括, 文件/ blobs)。

共同點:都是保存在瀏覽器端、且同源的
區別

Cookie localStorage sessionStorage indexedDB
容量大小 4kb左右 5M左右 5M左右 無限容量
過時時間 只在設置的過時時間以前一直有效,
即便窗口或者瀏覽器關閉
始終有效 當前瀏覽器窗口關閉前有效 始終有效
存儲方式 瀏覽器和服務器間來回傳遞 本地保存 本地保存 本地保存
做用域 在同源窗口中共享 在同源窗口中共享 在同源窗口而且同一窗口中共享 在同源窗口中共享

Web安全舉例

  • XSS(跨站腳本攻擊)幾種形式,防範手段,過濾哪些字符
  • csrf(跨站請求僞造)原理,實現,防範手段
  • sql注入
  • 命令行注入
  • DDoS(Distributed Denial of Service) 又叫分佈式拒絕服務
  • 流量劫持
    DNS劫持
    HTTP劫持
  • 服務器漏洞

  • https://juejin.im/entry/5a559dd36fb9a01c9e45d896

狀態碼

Web Worker

現代瀏覽器爲JavaScript創造的 多線程環境。能夠新建並將部分任務分配到worker線程並行運行,兩個線程可 獨立運行,互不干擾,可經過自帶的 消息機制 相互通訊。

限制:

  • 同源限制
  • 沒法使用 document / window / alert / confirm
  • 沒法加載本地資源

內存泄露

  • 意外的全局變量: 沒法被回收
  • 定時器: 未被正確關閉,致使所引用的外部變量沒法被釋放
  • 事件監聽: 沒有正確銷燬 (低版本瀏覽器可能出現)
  • 閉包: 會致使父級中的變量沒法被釋放
  • dom 引用: dom 元素被刪除時,內存中的引用未被正確清空

可用 chrome 中的 timeline 進行內存標記,可視化查看內存的變化狀況,找出異常點。

HTTP緩存機制

cookie和session的區別

常見兼容性問題(移動端/PC端)

polyfill的做用

代碼相關

44個 Javascript 題解析

https://lidaguang1989.github.io/2018/01/javascript-puzzlers/

43個 javascript 進階問題列表

https://github.com/lydiahallie/javascript-questions/blob/master/README-zh_CN.md

如何實現數組去重

正則實現trim()功能

function myTrim(str) {
let reg = /^\s+|\s+$/g;
return str.replace(reg, "");
}
console.log(myTrim('    asdf    '));

markdown源文件已上傳GitHub,若是以爲對你有所幫助,但願可以動動手指給個Star(O(∩_∩)O)。

相關文章
相關標籤/搜索