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

一年前寫了一篇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

  • +:jquery

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

  • {}.toString() === '[object object]'git

  • NaN !== NaN 、+undefined 爲 NaN

  • juejin.im/post/5a7172…

"Attribute"和"Property"的區別

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

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() // 返回與指定的選擇器組匹配的文檔中的元素列表
    複製代碼
  • developer.mozilla.org/zh-CN/docs/…

異步加載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 結果 緣由
store.company.com/dir2/other.… 成功 只有路徑不一樣
store.company.com/dir/inner/a… 成功 只有路徑不一樣
store.company.com/secure.html 失敗 不一樣協議 ( https和http )
store.company.com:81/dir/etc.htm… 失敗 不一樣端口 ( http:// 80是默認的)
news.company.com/dir/other.h… 失敗 不一樣域名 ( 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劫持

  • 服務器漏洞

  • juejin.im/entry/5a559…

狀態碼

Web Worker

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

限制:

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

內存泄露

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

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

HTTP緩存機制

cookie和session的區別

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

polyfill的做用

代碼相關

44個 Javascript 題解析

lidaguang1989.github.io/2018/01/jav…

43個 javascript 進階問題列表

github.com/lydiahallie…

如何實現數組去重

正則實現trim()功能

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

CSS篇

a標籤上四個僞類的執行順序是怎麼樣的?

link > visited > hover > active

  • L-V-H-A love hate 用喜歡和討厭兩個詞來方便記憶

css 屬性 content 有什麼做用?

content 屬性專門應用在 before/after 僞元素上,用於插入額外內容或樣式。可減小標籤的使用。

僞元素和僞類的區別和做用?

  • 僞元素 -- 在內容元素的先後插入額外的元素或樣式,可是這些元素實際上並不在文檔中生成。

  • 它們只在外部顯示可見,但不會在文檔的源代碼中找到它們,所以,稱爲「僞」元素。例如:

    p::before {content:"第一章:";}
      p::after {content:"Hot!";}
      p::first-line {background:red;}
      p::first-letter {font-size:30px;}
    複製代碼
  • 僞類 -- 將特殊的效果添加到特定選擇器上。它是已有元素上添加類別的,不會產生新的元素。例如: a:hover {color: #FF00FF} p:first-child {color: red}

::before 和 :after 中雙冒號和單冒號有什麼區別?

  • 在 CSS 中僞類一直用 : 表示,如 :hover, :active 等
  • 僞元素在CSS1中已存在,當時語法是用 : 表示,如 :before 和 :after
  • 後來在CSS3中修訂,僞元素用 :: 表示,如 ::before 和 ::after,以此區分僞元素和僞類
  • 因爲低版本IE對雙冒號不兼容,開發者爲了兼容性各瀏覽器,繼續使使用 :after 這種老語法表示僞元素
  • 綜上所述:::before 是 CSS3 中寫僞元素的新語法; :after 是 CSS1 中存在的、兼容IE的老語法

在CSS樣式中常使用 px、em 、rem的區別

  • px(絕對長度單位)
    • 在縮放頁面時沒法調整那些使用它做爲單位的字體、按鈕等的大小
  • em(相對長度單位)
    • 瀏覽器的默認字體都是16px,那麼1em=16px,以此類推計算10px=0.625em

    • 爲了簡化font-size的換算,通常都會在body中寫入如下代碼

      body { font-size: 62.5%; } /*  公式16px*62.5%=10px  */  
      複製代碼
    • em的值並非固定的

    • em會繼承父級元素的字體大小(參考物是父元素的font-size)

    • em中全部的字體都是相對於父元素的大小決定的;因此若是一個設置了font-size:1.2em的元素在另外一個設置了font-size:1.2em的元素裏,而這個元素又在另外一個設置了font-size:1.2em的元素裏,那麼最後計算的結果是1.2X1.2X1.2=1.728em

  • rem(相對長度單位)
    • rem單位可謂集相對大小和絕對大小的優勢於一身
    • 和em不一樣的是rem老是相對於根元素(如:root{}),而不像em同樣使用級聯的方式來計算尺寸。這種相對單位使用起來更簡單。
    • rem支持IE9及以上,意思是相對於根元素html(網頁),不會像em那樣,依賴於父元素的字體大小,而形成混亂。使用起來安全了不少。

你對 line-height 是如何理解的?

  • line-height 指一行字的高度,包含了字間距,其實是下一行基線到上一行基線距離
  • 若是一個標籤沒有定義 height 屬性,那麼其最終表現的高度是由 line-height 決定的
  • 一個容器沒有設置高度,那麼撐開容器高度的是 line-height 而不是容器內的文字內容
  • 把 line-height 值設置爲 height 同樣大小的值能夠實現單行文字的垂直居中
  • line-height 和 height 都能撐開一個高度,height 會觸發 haslayout,而 line-height 不會

line-height 三種賦值方式有何區別?(帶單位、純數字、百分比)

  • 帶單位:px 是固定值,而 em 會參考父元素 font-size 值計算自身的行高
  • 純數字:會把比例傳遞給後代。例如,父級行高爲 1.5,子元素字體爲 18px,則子元素行高爲 1.5 * 18 = 27px
  • 百分比:將計算後的值傳遞給後代

display: none; 與 visibility: hidden; 的區別

  • 聯繫:它們都能讓元素不可見
  • 區別:
    • display:none;會讓元素徹底從渲染樹中消失,渲染的時候不佔據任何空間;visibility: hidden;不會讓元素從渲染樹消失,渲染師元素繼續佔據空間,只是內容不可見
    • display: none;是非繼承屬性,子孫節點消失因爲元素從渲染樹消失形成,經過修改子孫節點屬性沒法顯示;visibility:hidden;是繼承屬性,子孫節點消失因爲繼承了hidden,經過設置visibility: visible;可讓子孫節點顯式
    • 修改常規流中元素的display一般會形成文檔重排。修改visibility屬性只會形成本元素的重繪
    • 讀屏器不會讀取display: none元素內容;會讀取visibility: hidden元素內容

rgba() 與 opacity的區別

  • rgba()
    • 僅僅改變的是背景的透明度
    • 不會對文本形成影響,不具備繼承性
  • opacity
    • 不只會改變背景的透明度
    • 還會改變文本的透明度,而且具備繼承性

盒模型的理解

頁面渲染時,dom 元素所採用的 佈局模型。可經過box-sizing進行設置。

  • content-box (W3C 標準盒模型)

默認值,標準盒子模型。 width 與 height 只包括內容的寬和高, 不包括邊框(border),內邊距(padding),外邊距(margin)。注意: 內邊距、邊框和外邊距都在這個盒子的外部。 好比說,.box {width: 350px; border: 10px solid black;} 在瀏覽器中的渲染的實際寬度將是 370px。

尺寸計算公式:

width = 內容的寬度

height = 內容的高度

寬度和高度的計算值都不包含內容的邊框(border)和內邊距(padding)。

  • border-box (IE盒模型)

width 和 height 屬性包括內容,內邊距和邊框,但不包括外邊距。這是當文檔處於 Quirks模式 時Internet Explorer使用的盒模型。注意,填充和邊框將在盒子內 , 例如, .box {width: 350px; border: 10px solid black;} 致使在瀏覽器中呈現的寬度爲350px的盒子。內容框不能爲負,而且被分配到0,使得不可能使用border-box使元素消失。

尺寸計算公式:

width = border + padding + 內容的寬度

height = border + padding + 內容的高度

relative、fixed、absolute和static四種定位的區別

通過定位的元素,其position屬性值必然是relative、absolute、fixed或sticky。

  • static:默認定位屬性值。該關鍵字指定元素使用正常的佈局行爲,即元素在文檔常規流中當前的佈局位置。此時 top, right, bottom, left 和 z-index 屬性無效。
  • relative:該關鍵字下,元素先放置在未添加定位時的位置,再在不改變頁面佈局的前提下調整元素位置(所以會在此元素未添加定位時所在位置留下空白)。
  • absolute:不爲元素預留空間,經過指定元素相對於最近的非 static 定位祖先元素的偏移,來肯定元素位置。絕對定位的元素能夠設置外邊距(margins),且不會與其餘邊距合併。
  • fixed:不爲元素預留空間,而是經過指定元素相對於屏幕視口(viewport)的位置來指定元素位置。元素的位置在屏幕滾動時不會改變。打印時,元素會出如今的每頁的固定位置。fixed 屬性會建立新的層疊上下文。當元素祖先的 transform 屬性非 none 時,容器由視口改成該祖先。
  • sticky:盒位置根據正常流計算(這稱爲正常流動中的位置),而後相對於該元素在流中的 flow root(BFC)和 containing block(最近的塊級祖先元素)定位。在全部狀況下(即使被定位元素爲 table 時),該元素定位均不對後續元素形成影響。當元素 B 被粘性定位時,後續元素的位置仍按照 B 未定位時的位置來肯定。position: sticky 對 table 元素的效果與 position: relative 相同。

block, inline和inline-block有什麼區別?

block inline-block inline
大小 填充其父容器的寬度 取決於內容 取決於內容
定位 重新的一行開始,而且不容許旁邊有 HTML 元素(除非是float) 與其餘內容一塊兒流動,並容許旁邊有其餘元素 與其餘內容一塊兒流動,並容許旁邊有其餘元素。
可否設置width和height 不能 設置會被忽略
可使用vertical-align對齊 不能夠 能夠 能夠
邊距(margin)和填充(padding) 各個方向都存在 各個方向都存在 只有水平方向存在。垂直方向會被忽略。 儘管border和padding在content周圍,但垂直方向上的空間取決於'line-height'
浮動(float) - - 就像一個block元素,能夠設置垂直邊距和填充。

flexbox佈局

柵格佈局(grid)

居中佈局

水平居中

  • 行內元素(inline-block): text-align: center
  • 塊級元素: margin: 0 auto
  • absolute + transform
  • flex + justify-content: center

垂直居中

  • line-height: height
  • absolute + transform
  • flex + align-items: center
  • table

水平垂直居中

  • absolute + transform
  • flex + justify-content + align-items

選擇器優先級

!important > 行內樣式 > #id > .class > tag > * > 繼承 > 默認

選擇器 從右往左 解析

  • 相同權重,定義最近者爲準:行內樣式 > 內部樣式 > 外部樣式
  • 含外部載入樣式時,後載入樣式覆蓋其前面的載入的樣式和內部樣式
  • 選擇器優先級: 行內樣式[1000] > id[100] > class[10] > Tag[1]
  • 在同一組屬性設置中,!important 優先級最高,高於行內樣式

解釋瀏覽器如何肯定哪些元素與 CSS 選擇器匹配

瀏覽器從最右邊的選擇器(關鍵選擇器)開始查找,根據關鍵選擇器,瀏覽器從 DOM 中篩選出元素,而後向上遍歷被選元素的父元素,判斷是否匹配。

例如,對於形如p span的選擇器,瀏覽器首先找到全部<span>元素,並遍歷它的父元素直到根元素以找到<p>元素。對於特定的<span>,只要找到一個<p>,就知道已經匹配並中止繼續匹配。

基於以上原理,爲了編寫高效CSS,應注意如下幾點:

  • 避免使用標籤和通用選擇器做爲關鍵選擇器。由於它們會匹配大量的元素,瀏覽器必需要進行大量的工做,去判斷這些元素的父元素們是否匹配
  • 選擇器匹配語句鏈越短,瀏覽器的匹配速度越快
  • 搞清楚哪些 CSS 屬性會觸發從新佈局(reflow)、重繪(repaint)和合成(compositing)。在寫樣式時,避免觸發從新佈局的可能。

CSS預處理器(Sass/Less)的優缺點分別是什麼?

  • CSS 預處理器基本思想:爲 CSS 增長了一些編程的特性(變量、邏輯判斷、函數等)
  • 開發者使用這種語言進行進行 Web 頁面樣式設計,再編譯成正常的 CSS 文件使用
  • 使用 CSS 預處理器,可使 CSS 更加簡潔、適應性更強、可讀性更佳,無需考慮兼容性
  • 最經常使用的 CSS 預處理器語言包括:Sass(SCSS)和 Less

優勢:

  • 提升 CSS 可維護性
  • 易於編寫嵌套選擇器
  • 引入變量,增添主題功能。能夠在不一樣的項目中共享主題文件
  • 經過混合(Mixins)生成可複用的 CSS
  • 將代碼分割成多個文件。不進行預處理的 CSS,雖然也能夠分割成多個文件,但須要創建多個 HTTP 請求加載這些文件

缺點:

  • 須要預處理工具
  • 從新編譯的時間可能會很慢

區別:

  • Less 用 JavaScript 實現,與 NodeJS 高度結合
  • Less 中,變量名稱以@做爲前綴,容易與 CSS 關鍵字混淆,如@media、@import和@font-face
  • 經過node-sass使用 Sass,它用 C ++ 編寫的 LibSass 綁定。在 Node 版本切換時,必須常常從新編譯。

link 與 @import 的區別

  • link功能較多,能夠定義 RSS,定義 Rel 等做用,而@import只能用於加載 css
  • 當解析到link時,頁面會同步加載所引的 css,而@import所引用的 css 會等到頁面加載完才被加載
  • @import須要 IE5 以上才能使用
  • 瀏覽器對 link 支持早於 @import ,可使用 @import 對老瀏覽器隱藏樣式
  • link可使用 js 動態引入,@import不行
  • @import 必須在樣式規則以前,能夠在css文件中引用其餘文件
  • 整體來講:link優於@import

常常遇到的瀏覽器的兼容性有哪些?緣由,解決方法是什麼,經常使用hack的技巧 ?

  • 瀏覽器默認的margin和padding不一樣。解決方案是加一個全局的*{margin:0;padding:0;}來統一
  • IE下,even對象有x,y屬性,可是沒有pageX,pageY屬性
  • Firefox下,event對象有pageX,pageY屬性,可是沒有x,y屬性

CSS優化、提升性能的方法有哪些?

  • 多個css合併,儘可能減小HTTP請求
  • 將css文件放在頁面最上面
  • 移除空的css規則
  • 避免使用CSS表達式
  • 選擇器優化嵌套,儘可能避免層級過深
  • 充分利用css繼承屬性,減小代碼量
  • 抽象提取公共樣式,減小代碼量
  • 屬性值爲0時,不加單位
  • 屬性值爲小於1的小數時,省略小數點前面的0
  • css雪碧圖

抽離樣式模塊怎麼寫,說出思路?

CSS能夠拆分紅2部分:公共CSS 和 業務CSS:

  • 網站的配色,字體,交互提取出爲公共CSS。這部分CSS命名不該涉及具體的業務
  • 對於業務CSS,須要有統一的命名,使用公用的前綴。能夠參考面向對象的CSS

什麼是響應式設計?響應式設計的基本原理是什麼?如何兼容低版本的IE?

  • 響應式設計就是網站可以兼容多個終端,而不是爲每一個終端作一個特定的版本

  • 基本原理是利用CSS3媒體查詢,爲不一樣尺寸的設備適配不一樣樣式

  • 對於低版本的IE,可採用JS獲取屏幕寬度,而後經過resize方法來實現兼容

    $(window).resize(function () {
          screenRespond();
      });
      screenRespond();
    
      function screenRespond(){
          var screenWidth = $(window).width();
          if(screenWidth <= 1800){
              $("body").attr("class", "w1800");
          }
          if(screenWidth <= 1400){
              $("body").attr("class", "w1400");
          }
          if(screenWidth > 1800){
              $("body").attr("class", "");
          }
      }
    複製代碼

用純CSS建立一個三角形的原理是什麼?

// 把上、左、右三條邊隱藏掉(顏色設爲 transparent)
#demo {
width: 0;
height: 0;
border-width: 20px;
border-style: solid;
border-color: transparent transparent red transparent;
}
複製代碼

什麼狀況下,用translate()而不用絕對定位?何時,狀況相反。

  • translate()是transform的一個值。改變transform或opacity不會觸發瀏覽器從新佈局(reflow)或重繪(repaint),只會觸發複合(compositions)
  • 改變絕對定位會觸發從新佈局,進而觸發重繪和複合
  • transform使瀏覽器爲元素建立一個 GPU 圖層,但改變絕對定位會使用到 CPU。
  • 所以translate()更高效,能夠縮短平滑動畫的繪製時間。

當使用translate()時,元素仍然佔據其原始空間(有點像position:relative),這與改變絕對定位不一樣。

對BFC規範(塊級格式化上下文:block formatting context)的理解?

對CSS浮動Float的理解

CSS動畫的理解

  • translate
  • transform
  • animation
  • transition

最後推薦一本由CSS大神Lea Verou著, CSS魔法譯的書

一本爲新一代CSS所寫的新一代CSS圖書。在我所知的技術專家中,沒人比Lea Verou更能領會新一代CSS的精髓。 ——Jeffrey Zeldman, 《網站重構》做者

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

相關文章
相關標籤/搜索