前端工程師自檢清單及答案整理

前言

根據大神們整理的前端自檢清單,本身整理一下答案,也方便本身學習。javascript

image.png

1、JavaScript基礎

變量和類型

1.JavaScript規定了幾種語言類型

js目前共定義了8種語言類型,其中包括:Undefined,Null,Boolean,Number,String,Object, Symbol,BigIntcss

2.JavaScript對象的底層數據結構是什麼

JavaScript基本類型數據都是直接按值存儲在棧中的(Undefined、Null、不是new出來的布爾、數字和字符串),每種類型的數據佔用的內存空間的大小是肯定的,並由系統自動分配和自動釋放。這樣帶來的好處就是,內存能夠及時獲得回收,相對於堆來講 ,更加容易管理內存空間。html

JavaScript引用類型數據被存儲於堆中 (如對象、數組、函數等,它們是經過拷貝和new出來的)。其實,說存儲於堆中,也不太準確,由於,引用類型的數據的地址指針是存儲於棧中的,當咱們想要訪問引用類型的值的時候,須要先從棧中得到對象的地址指針,而後,在經過地址指針找到堆中的所須要的數據。前端

3.Symbol類型在實際開發中的應用、可手動實現一個簡單的Symbol

1.使用Symbol來替代常量vue

const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
複製代碼

2.使用Symbol來做爲對象屬性名(key)java

const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
  [PROP_NAME]: "一斤代碼"
}
obj[PROP_AGE] = 18
複製代碼

3.使用Symbol定義類的私有屬性/方法node

// a.js
const PASSWORD = Symbol()
class Login {
  constructor(username, password) {
    this.username = username
    this[PASSWORD] = password
  }

  checkPassword(pwd) {
      return this[PASSWORD] === pwd
  }
}
export default Login
// b.js
import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456')  // true
login.PASSWORD  // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!
複製代碼

因爲Symbol常量PASSWORD被定義在a.js所在的模塊中,外面的模塊獲取不到這個Symbol,也不可能再建立一個如出一轍的Symbol出來(由於Symbol是惟一的),所以這個PASSWORD的Symbol只能被限制在a.js內部使用,因此使用它來定義的類屬性是沒有辦法被模塊外訪問到的,達到了一個私有化的效果。react

4.註冊和獲取全局Symbol window中建立的Symbol實例老是惟一的,而咱們須要的是在全部這些window環境下保持一個共享的Symbol。這種狀況下,咱們就須要使用另外一個API來建立或獲取Symbol,那就是Symbol.for(),它能夠註冊或獲取一個window間全局的Symbol實例:webpack

let gs1 = Symbol.for('global_symbol_1')  //註冊一個全局Symbol
let gs2 = Symbol.for('global_symbol_1')  //獲取全局Symbol
gs1 === gs2  // true
複製代碼

4.JavaScript中的變量在內存中的具體存儲形式

基本類型是保存在棧內存中的簡單數據段,它們的值都有固定的大小,保存在棧空間,經過按值訪問 引用類型是保存在堆內存中的對象,值大小不固定,棧內存中存放的該對象的訪問地址指向堆內存中的對象,JavaScript不容許直接訪問堆內存中的位置,所以操做對象時,實際操做對象的引用nginx

let a1 = 0; // 棧內存
let a2 = "this is string" // 棧內存
let a3 = null; // 棧內存
let b = { x: 10 }; // 變量b存在於棧中,{ x: 10 }做爲對象存在於堆中
let c = [1, 2, 3]; // 變量c存在於棧中,[1, 2, 3]做爲對象存在於堆中
複製代碼

image.png

5.基本類型對應的內置對象,以及他們之間的裝箱拆箱操做

內置對象: Object是 JavaScript 中全部對象的父對象 數據封裝類對象:Object、Array、Boolean、Number 和 String 其餘對象:Function、Math、Date、RegExp、Error。 特殊的基本包裝類型(String、Number、Boolean) arguments: 只存在於函數內部的一個類數組對象 裝箱: 把基本數據類型轉化爲對應的引用數據類型的操做,裝箱分爲隱式裝箱和顯示裝箱 隱式裝箱

let a = 'sun'
let b = a.indexof('s') // 0 // 返回下標
複製代碼

上面代碼在後臺實際的步驟爲:

let a = new String('sun')
let b = a.indexof('s')
a = null
複製代碼

實現機制:

1.建立String類型的一個實例; 2.在實例上調用指定的方法; 3.銷燬這個實例;

顯示裝箱 經過內置對象能夠對Boolean、Object、String等能夠對基本類型顯示裝箱 let a = new String('sun')

拆箱: 拆箱和裝箱相反,就是把引用類型轉化爲基本類型的數據,一般經過引用類型的valueof()和toString()方法實現

let name = new String('sun')
let age = new Number(24)
console.log(typeof name) // object
console.log(typeof age) //  object
// 拆箱操做
console.log(typeof age.valueOf()); // number // 24  基本的數字類型
console.log(typeof name.valueOf()); // string  // 'sun' 基本的字符類型
console.log(typeof age.toString()); // string  // '24' 基本的字符類型
console.log(typeof name.toString()); // string  // 'sun' 基本的字符類型
複製代碼

6.理解值類型和引用類型

image.png

7.null和undefined的區別

null表示"沒有對象",即該處不該該有值。典型用法是:

(1) 做爲函數的參數,表示該函數的參數不是對象。
(2) 做爲對象原型鏈的終點。

undefined表示"缺乏值",就是此處應該有一個值,可是尚未定義。典型用法是:

(1)變量被聲明瞭,但沒有賦值時,就等於undefined。
(2) 調用函數時,應該提供的參數沒有提供,該參數等於undefined。
(3)對象沒有賦值的屬性,該屬性的值爲undefined。
(4)函數沒有返回值時,默認返回undefined。

8.至少能夠說出三種判斷JavaScript數據類型的方式,以及他們的優缺點,如何準確的判斷數組類型

一、typeof:(能夠對基本類型作出準確的判斷,但對於引用類型,用它就有點力不從心了)

typeof 返回一個表示數據類型的字符串,返回結果包括:number、boolean、string、object、undefined、function等6種數據類型。

二、instanceof

instanceof是用來判斷A是否爲B的實例時,表達式爲:A instanceof B,若是 A是B的實例,則返回true; 不然返回false 在這裏特別注意的是 instanceof檢測的是原型

3 Object.prototype.toString

toString是Object原型對象上的一個方法,該方法默認返回其調用者的具體類型,更嚴格的講,是 toString運行時this指向的對象類型, 返回的類型格式爲[object,xxx],xxx是具體的數據類型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上全部對象的類型均可以經過這個方法獲取到。 4 constructor 查看對象對應的構造函數 construvtor在對應對象的原型下面,是自動生成的,當咱們寫一個構造函數的時候,程序自動添加,構造函數名.prototype.constructor = 構造函數名

image.png

9.可能發生隱式類型轉換的場景以及轉換原則,應如何避免或巧妙應用

image.png

10.出現小數精度丟失的緣由,JavaScript能夠存儲的最大數字、最大安全數字,JavaScript處理大數字的方法、避免精度丟失的方法

0.1+0.2不等於0.3,是由於計算機進行計算時先轉化成二進制,二浮點數用二進制表示時是無窮位的,IEEE754標準中用64位表示(1位用來表示符號位,11用來表示指數,52位表示尾數)會截斷後面的位數,再轉化成十進制,就有了偏差。

最大數字:

對於整數,前端出現問題的概率可能比較低,畢竟不多有業務須要須要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。

對於小數,前端出現問題的概率仍是不少的,尤爲在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)

最安全的數字-Math.pow(2, 53)-1,到+Math.pow(2, 53)-1

原型和原型鏈

1.instanceof的底層實現原理,手動實現一個instanceof

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表達式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表達式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    }
}
複製代碼

2.實現繼承的幾種方式以及他們的優缺點

// 原型繼承
function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
}
function SubType(){
    this.subProty =false;
}
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperValue())

// 借用構造函數
function SuperType(name) {
    this.name = name;
}
function SubType(){
    SuperType.call(this, 'demo');
    this.age = 18;
}
var instance = new SubType();
console.log(instance.name);
console.log(instance.age);

// 組合繼承
function SuperType(name){
    this.name = name;
    this.colors = ['red'];
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}
function SubType(name,age) {
    SuperType.call(this,name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    console.log(this.age);
}
var instance = new SubType('demo',18);
instance.sayAge();
instance.sayName();

// 原型式繼承
function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}
var person = {
    name: 'tom'
}
var anotherPerson = object(person)
console.log(anotherPerson.name)

// 寄生式繼承
function createAnother(original){
    var clone =Object.create(original);
    clone.sayHi = function () {
        console.log('hi');
    }
    return clone;
}
var person = {
    name: 'tom'
}
var anotherPerson = createAnother(person);
console.log(anotherPerson.name)
anotherPerson.sayHi();

// 寄生組合式繼承
function SuperType(name) {
    this.name = name;
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
function inheritPrototype(subType,superType){
    var prototype = Object.create(superType.prototype);
    prototype.constructor =subType;
    subType.prototype = prototype;
}
inheritPrototype(SubType,SuperType);
var person = new SubType('zhangsan',18);
person.sayName()
複製代碼

3.能夠描述new一個對象的詳細過程,手動實現一個new操做符

function Person (name,age){
    this.name = name;
    this.age = age;
    this.say = function () {
        console.log("I am " + this.name)
    }
}
function realizeNew(){
    let obj = {};
    let Con = [].shift.call(arguments);
    obj.__proto__ = Con.prototype;
    let result = Con.apply(obj,arguments);
    return typeof result === 'object'? result : obj
}
var person1 =realizeNew(Person,'張三')
複製代碼

4.理解es6 class構造以及繼承的底層實現原理

做用域和閉包

1.理解詞法做用域和動態做用域

詞法做用域,函數的做用域在函數定義的時候就決定了(取決於函數定義的位置)
動態做用域,函數的做用域在函數調用的時候就決定了(取決於函數的調用) js採用的是詞法做用域

2.理解JavaScript的做用域和做用域鏈

做用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。也就是說做用域最大的用處就是隔離變量,不一樣做用域下同名變量不會有衝突。 ES6 以前 JavaScript 沒有塊級做用域,只有全局做用域和函數做用域。ES6的到來,爲咱們提供了‘塊級做用域’,可經過新增命令let和const來體現。

function outFun2() {
    var inVariable = "內層變量2";
}
outFun2();//要先執行這個函數,不然根本不知道里面是啥
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
複製代碼

做用域鏈 在 JavaScript 中使用變量時,JavaScript 引擎將嘗試在當前做用域中查找變量的值。若是找不到變量,它將查找外部做用域並繼續這樣作,直到找到變量或到達全局做用域爲止。

若是仍然找不到變量,它將在全局做用域內隱式聲明變量(若是不是在嚴格模式下)或返回錯誤。 ####3.理解JavaScript的執行上下文棧,能夠應用堆棧信息快速定位問題 執行上下文是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念。

執行上下文的類型 執行上下文總共有三種類型
全局執行上下文:只有一個,瀏覽器中的全局對象就是 window 對象,this 指向這個全局對象。
函數執行上下文:存在無數個,只有在函數被調用的時候纔會被建立,每次調用函數都會建立一個新的執行上下文。
Eval 函數執行上下文: 指的是運行在 eval 函數中的代碼,不多用並且不建議使用。

執行棧 執行棧,也叫調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。 首次運行JS代碼時,會建立一個全局執行上下文並Push到當前的執行棧中。每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並Push到當前執行棧的棧頂。 根據執行棧LIFO規則,當棧頂函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文控制權將移到當前執行棧的下一個執行上下文。

執行上下文的建立 執行上下文分兩個階段建立:
1)建立階段;
2)執行階段
建立階段
一、肯定 this 的值,也被稱爲 This Binding。
二、LexicalEnvironment(詞法環境) 組件被建立。
三、VariableEnvironment(變量環境) 組件被建立。
This Binding:
在全局執行上下文中,this 的值指向全局對象,在瀏覽器中,this 的值指向 window 對象。 在函數執行上下文中,this 的值取決於函數的調用方式。若是它被一個對象引用調用,那麼 this 的值被設置爲該對象,不然 this 的值被設置爲全局對象或 undefined(嚴格模式下)
詞法環境
在詞法環境中,有兩個組成部分:
(1)環境記錄(environment record)
(2)對外部環境的引用 環境記錄是存儲變量和函數聲明的實際位置。 對外部環境的引用意味着它能夠訪問其外部詞法環境。 變量環境:
它也是一個詞法環境,其 EnvironmentRecord 包含了由 VariableStatements 在此執行上下文建立的綁定。 如上所述,變量環境也是一個詞法環境,所以它具備上面定義的詞法環境的全部屬性。 在 ES6 中,LexicalEnvironment 組件和 VariableEnvironment 組件的區別在於前者用於存儲函數聲明和變量( let 和 const )綁定,然後者僅用於存儲變量( var )綁定。 在建立階段,代碼會被掃描並解析變量和函數聲明,其中函數聲明存儲在環境中,而變量會被設置爲 undefined(在 var 的狀況下)或保持未初始化(在 let 和 const 的狀況下) 這就是爲何你能夠在聲明以前訪問 var 定義的變量(儘管是 undefined ),但若是在聲明以前訪問 let 和 const 定義的變量就會提示引用錯誤的緣由。

這就是咱們所謂的變量提高。

4.this的原理以及幾種不一樣使用場景的取值

1、this原理

this 既不指向函數自身,也不指函數的詞法做用域,而是調用函數時的對象!

2、使用場景

一)普通函數的調用,this指向的是Window

var name = '卡卡';
function cat(){
    var name = '有魚';
    console.log(this.name);//卡卡
    console.log(this);//Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
}
cat();
複製代碼

(二)對象的方法,this指的是該對象

一、一層做用域鏈時,this指的該對象

var name = '卡卡';
var cat = {
    name:'有魚',
    eat:function(){
        console.log(this.name);//有魚
    }
}
cat.eat();
複製代碼

二、多層做用域鏈時,this指的是距離方法最近的一層對象

var name = '卡卡';
var cat = {
    name:'有魚',
    eat1:{
        name:'年年',
        eat2:function(){
            console.log(this.name);//年年
        }
    }
}
cat.eat1.eat2();
複製代碼

這裏須要注意一個狀況,若是cat.eat1.eat2這個結果賦值給一個變量eat3,則eat3()的值是多少呢?

var eat3 = cat.eat1.eat2;
eat3(); // 卡卡
複製代碼

答案是[卡卡],這個是由於通過賦值操做時,並未發起函數調用,eat3()這個纔是真正的調用,而發起這個調用的是根對象window,因此this指的就是window,this.name=卡卡

(三)構造函數的調用,this指的是實例化的新對象

var name = '卡卡';
function Cat(){
    this.name = '有魚';
    this.type = '英短藍貓';
}
var cat1 = new Cat();
console.log(cat1);// 實例化新對象 Cat {name: "有魚", type: "英短藍貓"}
console.log(cat1.name);// 有魚
複製代碼

(四)apply和call調用時,this指向參數中的對象

var name = '有魚';
function eat(){
    console.log(this.name);
}
var cat = {
    name:'年年',
}
var dog = {
    name:'高飛',
}

eat.call(cat);// 年年
eat.call(dog);// 高飛
複製代碼

(五)匿名函數調用,指向的是全局對象

var name = '卡卡';
var cat = {
    name:'有魚',
    eat:(function(){
        console.log(this.name);//卡卡
    })()
}
cat.eat;
複製代碼

(六)定時器中調用,指向的是全局變量

var name = '卡卡';
var cat = setInterval(function(){
    var name = '有魚';
    console.log(this.name);// 卡卡
    clearInterval(cat);
},500);
複製代碼

總結: ①普通函數的調用,this指向的是window
②對象方法的調用,this指的是該對象,且是最近的對象
③構造函數的調用,this指的是實例化的新對象
④apply和call調用,this指向參數中的對象
⑤匿名函數的調用,this指向的是全局對象window
⑥定時器中的調用,this指向的是全局變量window

5.閉包的實現原理和做用,能夠列舉幾個開發中閉包的實際應用

包就是可以讀取其餘函數內部變量的函數 它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000
複製代碼

在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證實了,函數f1中的局部變量n一直保存在內存中,並無在f1調用後被自動清除。 爲何會這樣呢?緣由就在於f1是f2的父函數,而f2被賦給了一個全局變量,這致使f2始終在內存中,而f2的存在依賴於f1,所以f1也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。 這段代碼中另外一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,所以nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數自己也是一個閉包,因此nAdd至關因而一個setter,能夠在函數外部對函數內部的局部變量進行操做。

6.理解堆棧溢出和內存泄漏的原理,如何防止

一、內存泄露:是指申請的內存執行完後沒有及時的清理或者銷燬,佔用空閒內存,內存泄露過多的話,就會致使後面的程序申請不到內存。所以內存泄露會致使內部內存溢出 二、堆棧溢出:是指內存空間已經被申請完,沒有足夠的內存提供了

常見的手段是將一個變量置爲null,該變量就會被下一輪垃圾回收機制回收。 常見的內存泄露的緣由:

  • 全局變量引發的內存泄露
  • 閉包
  • 沒有被清除的計時器 解決方法:
  • 減小沒必要要的全局變量
  • 嚴格使用閉包(由於閉包會致使內存泄露)
  • 避免死循環的發生

執行機制

1.JavaScript如何實現異步編程,能夠詳細描述EventLoop機制

image.png
任務隊列實際上有兩個,一個是宏任務隊列,一個是微任務隊列,當主線程執行完畢,若是微任務隊列中有微任務,則會先進入執行棧,當微任務隊列沒有任務時,纔會執行宏任務的隊列。

2.宏任務和微任務分別有哪些

微任務包括: 原生Promise(有些實現的promise將then方法放到了宏任務中),Object.observe(已廢棄), MutationObserver, MessageChannel;只有promise調用then的時候,then裏面的函數纔會被推入微任務中 宏任務包括:setTimeout, setInterval, setImmediate, I/O;

3.使用Promise實現串行

function execute(tasks){
    return tasks.reduce((previousPromise, currentPromise)=>previousPromise.then(resultList=>{
        return new Promise(resolve=>{
            currentPromise().then(result=>{
                resolve(resultList.concat(result))
            }).catch(()=>{
                resolve(resultList.concat(null))
            })
        })
    },Promise.resolve([])))
}
const execute = (tasks = []) => {
    const resultList = [];
    for(task of tasks){
        try{
            resultList.push(await tasks())
        }catch(err){
            resultList.push(null);
        }
    }
    return resultList;
}
複製代碼

4.Node與瀏覽器EventLoop的差別

Node 10之前: 執行完一個階段的全部任務 執行完nextTick隊列裏面的內容 而後執行完微任務隊列的內容 Node 11之後: 和瀏覽器的行爲統一了,都是每執行一個宏任務就執行完微任務隊列。

5.如何在保證頁面運行流暢的狀況下處理海量數據

語法和API

1.理解ECMAScript和JavaScript的關係

ECMAScript和JavaScript的關係是,前者是後者的規格,後者是前者的一種實現

2.熟練運用es五、es6提供的語法規範,

3.熟練掌握JavaScript提供的全局對象(例如Date、Math)、全局函數(例如decodeURI、isNaN)、全局屬性(例如Infinity、undefined)

4.熟練應用map、reduce、filter 等高階函數解決問題

5.setInterval須要注意的點,使用settimeout實現setInterval

function  mySetInterval(fn,mil){
    function interval(){
        setTimeout(interval,mil);
        fn();
    }
    setTimeout(interval,mil)
}
mySetInterval(function(){console.log(1)},1000)
複製代碼

6.JavaScript提供的正則表達式API、可使用正則表達式(郵箱校驗、URL解析、去重等)解決常見問題

7.JavaScript異常處理的方式,統一的異常處理方案

2、HTML和CSS

HTML

1.從規範的角度理解HTML,從分類和語義的角度使用標籤

2.經常使用頁面標籤的默認樣式、自帶屬性、不一樣瀏覽器的差別、處理瀏覽器兼容問題的方式

3.元信息類標籤(head、title、meta)的使用目的和配置方法

4.HTML5離線緩存原理

5.可使用Canvas API、SVG等繪製高性能的動畫

CSS

1.CSS盒模型,在不一樣瀏覽器的差別

1. W3C 標準盒模型: 屬性width,height只包含內容content,不包含border和padding。 2. IE 盒模型: 屬性width,height包含border和padding,指的是content+padding+border。 css的盒模型由content(內容)、padding(內邊距)、border(邊框)、margin(外邊距)組成。但盒子的大小由content+padding+border這幾部分決定,把margin算進去的那是盒子佔據的位置,而不是盒子的大小! 咱們在編寫頁面代碼時應儘可能使用標準的W3C模型(需在頁面中聲明DOCTYPE類型),這樣能夠避免多個瀏覽器對同一頁面的不兼容。 由於若不聲明DOCTYPE類型,IE瀏覽器會將盒子模型解釋爲IE盒子模型,FireFox等會將其解釋爲W3C盒子模型;若在頁面中聲明瞭DOCTYPE類型,全部的瀏覽器都會把盒模型解釋爲W3C盒模型。

2.CSS全部選擇器及其優先級、使用場景,哪些能夠繼承,如何運用at規則

3.CSS僞類和僞元素有哪些,它們的區別和實際應用

image.png
image.png

4.HTML文檔流的排版規則,CSS幾種定位的規則、定位參照物、對文檔流的影響,如何選擇最好的定位方式,雪碧圖實現原理

5.水平垂直居中的方案、能夠實現6種以上並對比它們的優缺點

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .parent1{
            width: 200px;
            height: 200px;
            background: red;
            position: relative;
        }
        .parent2{
            display: table-cell;
            vertical-align: middle;
            text-align: center;
        }
        .parent5{
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .child1{
            width: 100px;
            height: 100px;
            position: absolute;
            left: 50%;
            top:50%;
            margin-left: -50px;
            margin-top: -50px;
            line-height: 100px;
            text-align: center;
        }
        .child2{
            position: absolute;
            left: 50%;
            top:50%;
            transform: translate(-50%,-50%);
        }
        .child3{
            position: absolute;
            left: 0;
            top: 0;
            right: 0;
            bottom: 0;
            margin: auto;
            width: 100px;
            height: 100px;
            line-height: 100px;
            text-align: center;
        }
        .child4{
            display: inline-block;
            width: 100px;
            height: 50px;
            overflow: scroll;
        }
        .child6{
            width: 100px;
            height: 100px;
            display: inline-block;
            text-align: center;
            vertical-align: middle;
            line-height: 100px;
        }
        .parent6{
            text-align: center;
        }
        .parent6::after{
            content: '';
            height: 100%;
            vertical-align: middle;
            display: inline-block;
        }
    </style>
</head>
<body>
    <p>方案一:知道寬度的狀況下 absolute+margin負值</p>
    <div class="parent1">
        <div class="child1">child1</div>
    </div>
    <p>方案二:不知道寬度的狀況下 absolute+transform</p>
    <div class="parent1">
        <div class="child2">child2</div>
    </div>
    <p>方案三:不知道寬度的狀況下 absolute+margin:auto</p>
    <div class="parent1">
        <div class="child3">child3</div>
    </div>
    <p>方案四:多行文本垂直居中 table-cell vertical-align:middle</p>
    <div class="parent1 parent2">
        <div class="child4">多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle</div>
    </div>
    <p>方案五:display:flex</p>
    <div class="parent1 parent5">
        <div class="child5">flex</div>
    </div>
    <p>方案六:僞元素</p>
    <div class="parent1 parent6">
        <div class="child6">僞元素</div>
    </div>
</body>
</html>
複製代碼

6.BFC實現原理,能夠解決的問題,如何建立BFC

juejin.im/post/5ecdd5…

7.可以使用CSS函數複用代碼,實現特殊效果

8.PostCSS、Sass、Less的異同,以及使用配置,至少掌握一種

9.CSS模塊化方案、如何配置按需加載、如何防止CSS阻塞渲染

10.熟練使用CSS實現常見動畫,如漸變、移動、旋轉、縮放等等

11.CSS瀏覽器兼容性寫法,瞭解不一樣API在不一樣瀏覽器下的兼容性狀況

12.掌握一套完整的響應式佈局方案

3、計算機基礎

編譯原理

1.理解代碼究竟是什麼,計算機如何將代碼轉換爲能夠運行的目標程序

js是一門解釋型語言(英語:Interpreted language),是一種編程語言。這種類型的編程語言,會將代碼一句一句直接運行,不須要像編譯語言(Compiled language)同樣,通過編譯器先行編譯爲機器碼,以後再運行。這種編程語言須要利用解釋器,在運行期,動態將代碼逐句解釋(interpret)爲機器碼,或是已經預先編譯爲機器碼的的子程序,以後再運行。

2.正則表達式的匹配原理和性能優化

3.如何將JavaScript代碼解析成抽象語法樹(AST)

4.base64的編碼原理

5.幾種進制的相互轉換計算方法,在JavaScript中如何表示和轉換

網絡協議

1.理解什麼是協議,瞭解TCP/IP網絡協議族的構成,每層協議在應用程序中發揮的做用

2.三次握手和四次揮手詳細原理,爲何要使用這種機制

3.有哪些協議是可靠,TCP有哪些手段保證可靠交付

4.DNS的做用、DNS解析的詳細過程,DNS優化原理

5.CDN的做用和原理

6.HTTP請求報文和響應報文的具體組成,能理解常見請求頭的含義,有幾種請求方式,區別是什麼

7.HTTP全部狀態碼的具體含義,看到異常狀態碼能快速定位問題

8.HTTP1.一、HTTP2.0帶來的改變

9.HTTPS的加密原理,如何開啓HTTPS,如何劫持HTTPS請求

10.理解WebSocket協議的底層原理、與HTTP的區別

###設計模式

1.熟練使用前端經常使用的設計模式編寫代碼,如單例模式、裝飾器模式、代理模式等

2.發佈訂閱模式和觀察者模式的異同以及實際應用

3.能夠說出幾種設計模式在開發中的實際應用,理解框架源碼中對設計模式的應用

4、數據結構和算法

JavaScript編碼能力

1.多種方式實現數組去重、扁平化、對比優缺點

2.多種方式實現深拷貝、對比優缺點

3.手寫函數柯里化工具函數、並理解其應用場景和優點

4.手寫防抖和節流工具函數、並理解其內部原理和應用場景

5.實現一個sleep函數

手動實現前端輪子

1.手動實現call、apply、bind

Function.prototype.myCall = function (context = window,...args) {
    context.fn = this;
    console.log(args)
    let result = context.fn(...args);
    delete context.fn;
    return result;
}
Function.prototype.myApply = function (context = window, arr) {
    context.fn = this;
    let result = !arr ? context.fn() : context.fn(...arr);
    delete context.fn;
    return result;
}
Function.prototype.myBind = function (context = window , ...arg) {
    context.fn = this;
    let bound = function () {
        let args = [...args].concat(...arguments);
        context.fn = this instanceof context.fn ? this : context;
        let result = context.fn(...args);
        delete context.fn;
        return result;
    }
    bound.prototype = new this();
    return bound;
}
複製代碼

2.手動實現符合Promise/A+規範的Promise、手動實現async await

function handlePromise(promise2, x, resolve, reject){
    if (promise2 === x) { //promise2是否等於x,也就是判斷是否將本身自己返回
        return reject(new TypeError('circular reference')); //若是是拋出錯誤
    }
    if(x !==null && (typeof x==='object' || typeof x ==='function')) {
        let called; //called控制resolve或reject 只執行一次,屢次調用沒有任何做用。
        try{
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x,y=>{
                    if(called) return;
                    called = true;
                    handlePromise(promise2,y,resolve,reject)
                },r => {
                    if(called) return;
                    called = true;
                    reject(r);
                })
            } else {
                reject(x);
            }
        }catch(err) {
            if (called) return;
            called = true;
            reject(err);
        }
    } else {
        reject(x);
    }
}
class Promise {
    constructor(executor){
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve =  (value) => {
            if(this.status === 'pending') {
                this.value = value;
                this.status = 'resolved';
                this.onResolvedCallbacks.foEach(fn => fn(this.value));
            }
        }
        let reject = (reason) => {
            if(this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedCallbacks.foEach(fn => fn(this.reason));
            }
        }
        try{
            executor(resolve,reject)
        }catch(err){
            reject(err);
        }
        
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
        let promise2; // 返回的新的promise
        if (this.status === 'pending') {
            this.onResolvedCallbacks.push()
            promise2 = new Promise((resolve,reject)=>{
                this.onResolvedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onFulfilled(this.value);
                            handlePromise(promise2,x,resolve,reject)
                        }catch(err){
                            reject(err);
                        }
                    },0)
                    
                })
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onRejected(this.reason);
                            handlePromise(promise2,x,resolve,reject)
                        }catch(err){
                            reject(err);
                        }
                    },0)
                    
                })
            })
        }
        if (this.status === 'resolved') {
            promise2 = new Promise((resolve,reject) =>{
                setTimeout(()=>{
                    try{
                        let x =onFulfilled(this.value);
                        handlePromise(promise2,x,resolve,reject)
                    }catch(err){
                        reject(err);
                    }
                },0)
                
            })
        }
        if(this.status === 'rejected') {
            onRejected(this.reason);
            promise2 = new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    try{
                        let x = onRejected(this.reason);
                        handlePromise(promise2,x,resolve,reject)
                    }catch(err){
                        reject(err);
                    }
                },0)
                
            })
        }
        return promise2;
    }
    catch(onRejected){ //在此處添加原型上的方法catch
        return this.then(null,onRejected);
    }
}
Promise.all = function (promiseArrs) { //在Promise類上添加一個all方法,接受一個傳進來的promise數組
    return new Promise((resolve, reject) => { //返回一個新的Promise
        let arr = []; //定義一個空數組存放結果
        let i = 0;
        function handleData(index, data) { //處理數據函數
            arr[index] = data;
            i++;
            if (i === promiseArrs.length) { //當i等於傳遞的數組的長度時 
                resolve(arr); //執行resolve,並將結果放入
            }
        }
        for (let i = 0; i < promiseArrs.length; i++) { //循環遍歷數組
            promiseArrs[i].then((data) => {
                handleData(i, data); //將結果和索引傳入handleData函數
            }, reject)
        }
    })
}
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve, reject);
        }
    })
}
Promise.resolve = function (val) {
    return new Promise((resolve, reject) => resolve(val));
}
Promise.reject = function (val) {
    return new Promise((resolve, reject) => reject(val));
}
module.exports = Promise;
複製代碼

3.手寫一個EventEmitter實現事件發佈、訂閱

class EventEmitter {
    constructor(){
        this.events = {}
    }
    on(eventName,callback){
        if(this.events[eventName]){
            this.events[eventName].push(callback)
        } else{
            this.events[eventName] = [callback]
        }
    }
    emit(eventName,...rest){
        if(this.events[eventName]){
            this.events[eventName].forEach(fn=>fn.apply(this,rest))
        }
    }
}
const event =new EventEmitter();
const handle = (...pyload) => console.log(pyload)
event.on('click',handle)
event.emit('click',1,2,3)
複製代碼

4.能夠說出兩種實現雙向綁定的方案、能夠手動實現

5.手寫JSON.stringify、JSON.parse

window.JSON = {
    parse: function (str) {
        return eval('(' + str + ')')   //防止{}會返回undefined
    },
    stringify: function (str) {
        if (typeof str === 'number') {
            return Number(str);
        }
        if (typeof str === 'string') {
            return str
        };
        var s = '';
        console.log(Object.prototype.toString.call(str))
        switch (Object.prototype.toString.call(str)) {
            case '[object Array]':
                s += '[';
                for (var i = 0; i < str.length - 1; i++) {
                    if (typeof str === 'string') {
                        s += '"' + str[i] + '",'
                    } else {
                        s += str[i] + ','
                    }
                }
                if (typeof str[str.length - 1] == 'string') {
                    s += '"' + str[i] + '"'
                } else {
                    if (str[str.length - 1] == null) {
                        str[str.length - 1] = null;
                        s += 'null';
                    } else {
                        s += (str[str.length - 1] ? str[str.length - 1] : '')
                    }
                }
                s += "]";
                break;
            case '[object Date]':
                console.log(str.toJSON())
                s+='"'+(str.toJSON?str.toJSON():str.toString())+'"';
                break;
            case '[object Function]':
                s= 'undefined';
                break
            case '[object Object]':
                s+='{'
                for(var key in str) {
                    if(str[key] === undefined){
                        continue;
                    }
                    if(typeof str[key] === 'symbol' || typeof str[key] === 'function') {
                        continue;
                    }
                    if(typeof Object.prototype.toString.call(str[key]) === '[object RegExp]') {
                        continue;
                    }
                    s+=('"'+key+'":"'+str[key]+'",')
                } 
                s = s.slice(0,s.length-1);
                if(s===''){s+='{'}
                s+='}'
                break   

        }
        return s;
    }
}
複製代碼

6.手寫一個模版引擎,並能解釋其中原理

7 .手寫懶加載、下拉刷新、上拉加載、預加載等效果

數據結構

1.理解常見數據結構的特色,以及他們在不一樣場景下使用的優缺點

2.理解數組、字符串的存儲原理,並熟練應用他們解決問題

3.理解二叉樹、棧、隊列、哈希表的基本結構和特色,並能夠應用它解決問題

4.瞭解圖、堆的基本結構和使用場景

算法

1.可計算一個算法的時間複雜度和空間複雜度,可估計業務邏輯代碼的耗時和內存消耗

2.至少理解五種排序算法的實現原理、應用場景、優缺點,可快速說出時間、空間複雜度

3.瞭解遞歸和循環的優缺點、應用場景、並可在開發中熟練應用

4.可應用回溯算法、貪心算法、分治算法、動態規劃等解決複雜問題

5.前端處理海量數據的算法方案

5、運行環境

瀏覽器API

1.瀏覽器提供的符合W3C標準的DOM操做API、瀏覽器差別、兼容性

2.瀏覽器提供的瀏覽器對象模型 (BOM)提供的全部全局API、瀏覽器差別、兼容性

3.大量DOM操做、海量數據的性能優化(合併操做、Diff、requestAnimationFrame等)

4.瀏覽器海量數據存儲、操做性能優化

5.DOM事件流的具體實現機制、不一樣瀏覽器的差別、事件代理

6.前端發起網絡請求的幾種方式及其底層實現、能夠手寫原生ajax、fetch、能夠熟練使用第三方庫

7.瀏覽器的同源策略,如何避免同源策略,幾種方式的異同點以及如何選型

8.瀏覽器提供的幾種存儲機制、優缺點、開發中正確的選擇

9.瀏覽器跨標籤通訊

瀏覽器原理

1.各瀏覽器使用的JavaScript引擎以及它們的異同點、如何在代碼中進行區分

2.請求數據到請求結束與服務器進行了幾回交互

3.可詳細描述瀏覽器從輸入URL到頁面展示的詳細過程

4.瀏覽器解析HTML代碼的原理,以及構建DOM樹的流程

5.瀏覽器如何解析CSS規則,並將其應用到DOM樹上

6.瀏覽器如何將解析好的帶有樣式的DOM樹進行繪製

7.瀏覽器的運行機制,如何配置資源異步同步加載

8.瀏覽器迴流與重繪的底層原理,引起緣由,如何有效避免

當Render Tree中部分或所有元素的尺寸、結構、或某些屬性發生改變時,瀏覽器從新渲染部分或所有文檔的過程稱爲迴流會致使迴流的操做:

  • 頁面首次渲染
  • 瀏覽器窗口大小發生改變
  • 元素尺寸或位置發生改變
  • 元素內容變化(文字數量或圖片大小等等)
  • 元素字體大小變化
  • 添加或者刪除可見的DOM元素
  • 激活CSS僞類(例如::hover)
  • 查詢某些屬性或調用某些方法

當頁面中元素樣式的改變並不影響它在文檔流中的位置時(例如:color、background-color、visibility等),瀏覽器會將新樣式賦予給元素並從新繪製它,這個過程稱爲重繪CSS

  • 避免使用table佈局。
  • 儘量在DOM樹的最末端改變class。
  • 避免設置多層內聯樣式。
  • 將動畫效果應用到position屬性爲absolute或fixed的元素上。
  • 避免使用CSS表達式(例如:calc())。

JavaScript

  • 避免頻繁操做樣式,最好一次性重寫style屬性,或者將樣式列表定義爲class並一次性更改class屬性。
  • 避免頻繁操做DOM,建立一個documentFragment,在它上面應用全部DOM操做,最後再把它添加到文檔中。
  • 也能夠先爲元素設置display: none,操做結束後再把它顯示出來。由於在display屬性爲none的元素上進行的DOM操做不會引起迴流和重繪。
  • 避免頻繁讀取會引起迴流/重繪的屬性,若是確實須要屢次使用,就用一個變量緩存起來。
  • 對具備複雜動畫的元素使用絕對定位,使它脫離文檔流,不然會引發父元素及後續元素頻繁迴流。
  • 9.瀏覽器的垃圾回收機制,如何避免內存泄漏 垃圾收集機制的原理 垃圾收集器會按照固定的時間間隔,週期性的找出再也不繼續使用的變量,而後釋放其佔用的內存。

什麼叫再也不繼續使用的變量?

再也不使用的變量也就是生命週期結束的變量,是局部變量,局部變量只在函數的執行過程當中存在,當函數運行結束,沒有其餘引用(閉包),那麼該變量會被標記回收。

全局變量的生命週期直至瀏覽器卸載頁面纔會結束,也就是說全局變量不會被當成垃圾回收。

工做原理:

當變量進入環境時(例如在函數中聲明一個變量),將這個變量標記爲「進入環境」,當變量離開環境時,則將其標記爲「離開環境」。標記「離開環境」的就回收內存。

工做流程:

  1. 垃圾收集器會在運行的時候會給存儲在內存中的全部變量都加上標記。
  2. 去掉環境中的變量以及被環境中的變量引用的變量的標記。
  3. 那些還存在標記的變量被視爲準備刪除的變量。
  4. 最後垃圾收集器會執行最後一步內存清除的工做,銷燬那些帶標記的值並回收它們所佔用的內存空間。

到2008年爲止,IE、Chorme、Fireofx、Safari、Opera 都使用標記清除式的垃圾收集策略,只不過垃圾收集的時間間隔互有不一樣

避免內存泄漏的方法

  • 少用全局變量,避免意外產生全局變量
  • 使用閉包要及時注意,有Dom元素的引用要及時清理。
  • 計時器裏的回調沒用的時候要記得銷燬。
  • 爲了不疏忽致使的遺忘,咱們可使用 WeakSet 和 WeakMap結構,它們對於值的引用都是不計入垃圾回收機制的,表示這是弱引用。 舉個例子:
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
複製代碼

複製代碼這種狀況下,一旦消除對該節點的引用,它佔用的內存就會被垃圾回收機制釋放。Weakmap 保存的這個鍵值對,也會自動消失。

####10.瀏覽器採用的緩存方案,如何選擇和控制合適的緩存方案 緩存過程分析 瀏覽器與服務器通訊的方式爲應答模式,便是:瀏覽器發起HTTP請求 – 服務器響應該請求。那麼瀏覽器第一次向服務器發起該請求後拿到請求結果,會根據響應報文中HTTP頭的緩存標識,決定是否緩存結果,是則將請求結果和緩存標識存入瀏覽器緩存中,簡單的過程以下圖:

image.png
由上圖咱們能夠知道:

  • 瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識

  • 瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中

    以上兩點結論就是瀏覽器緩存機制的關鍵,他確保了每一個請求的緩存存入與讀取,只要咱們再理解瀏覽器緩存的使用規則,那麼全部的問題就迎刃而解了,本文也將圍繞着這點進行詳細分析。爲了方便你們理解,這裏咱們根據是否須要向服務器從新發起HTTP請求將緩存過程分爲兩個部分,分別是強制緩存和協商緩存 。

強制緩存

強制緩存就是向瀏覽器緩存查找該請求結果,並根據該結果的緩存規則來決定是否使用該緩存結果的過程,強制緩存的狀況主要有三種(暫不分析協商緩存過程),以下:

  • 不存在該緩存結果和緩存標識,強制緩存失效,則直接向服務器發起請求(跟第一次發起請求一致),以下圖:

    image.png

  • 存在該緩存結果和緩存標識,但該結果已失效,強制緩存失效,則使用協商緩存(暫不分析),以下圖

    image.png

  • 存在該緩存結果和緩存標識,且該結果還沒有失效,強制緩存生效,直接返回該結果,以下圖

    image.png
    當瀏覽器向服務器發起請求時,服務器會將緩存規則放入HTTP響應報文的HTTP頭中和請求結果一塊兒返回給瀏覽器,控制強制緩存的字段分別是Expires和Cache-Control,其中Cache-Control優先級比Expires高。 Expires Expires是HTTP/1.0控制網頁緩存的字段,其值爲服務器返回該請求結果緩存的到期時間,即再次發起該請求時,若是客戶端的時間小於Expires的值時,直接使用緩存結果。 Cache-Control 在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存,主要取值爲:

  • public:全部內容都將被緩存(客戶端和代理服務器均可緩存)

  • private:全部內容只有客戶端能夠緩存,Cache-Control的默認取值

  • no-cache:客戶端緩存內容,可是是否使用緩存則須要通過協商緩存來驗證決定

  • no-store:全部內容都不會被緩存,即不使用強制緩存,也不使用協商緩存

  • max-age=xxx (xxx is numeric):緩存內容將在xxx秒後失效 到了HTTP/1.1,Expire已經被Cache-Control替代,緣由在於Expires控制緩存的原理是使用客戶端的時間與服務端返回的時間作對比,那麼若是客戶端與服務端的時間由於某些緣由(例如時區不一樣;客戶端和服務端有一方的時間不許確)發生偏差,那麼強制緩存則會直接失效,這樣的話強制緩存的存在則毫無心義,

因爲Cache-Control的優先級比expires,那麼直接根據Cache-Control的值進行緩存,意思就是說在600秒內再次發起該請求,則會直接使用緩存結果,強制緩存生效。

注:在沒法肯定客戶端的時間是否與服務端的時間同步的狀況下,Cache-Control相比於expires是更好的選擇,因此同時存在時,只有Cache-Control生效。

瀏覽器的緩存存放在哪裏,如何在瀏覽器中判斷強制緩存是否生效?

  • 內存緩存(from memory cache):內存緩存具備兩個特色,分別是快速讀取和時效性: 快速讀取:內存緩存會將編譯解析後的文件,直接存入該進程的內存中,佔據該進程必定的內存資源,以方便下次運行使用時的快速讀取。 時效性:一旦該進程關閉,則該進程的內存則會清空。

  • 硬盤緩存(from disk cache):硬盤緩存則是直接將緩存寫入硬盤文件中,讀取緩存須要對該緩存存放的硬盤文件進行I/O操做,而後從新解析該緩存內容,讀取複雜,速度比內存緩存慢。

在瀏覽器中,瀏覽器會在js和圖片等文件解析執行後直接存入內存緩存中,那麼當刷新頁面時只需直接從內存緩存中讀取(from memory cache);而css文件則會存入硬盤文件中,因此每次渲染頁面都須要從硬盤讀取緩存(from disk cache)。

協商緩存

協商緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有如下兩種狀況:

  • 協商緩存生效,返回304,以下
    image.png
  • 協商緩存失效,返回200和請求結果結果,以下
    image.png
    一樣,協商緩存的標識也是在響應報文的HTTP頭中和請求結果一塊兒返回給瀏覽器的,控制協商緩存的字段分別有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的優先級比Last-Modified / If-Modified-Since高。 Last-Modified / If-Modified-Since
  • Last-Modified是服務器響應請求時,返回該資源文件在服務器最後被修改的時間,以下。
    image.png
  • If-Modified-Since則是客戶端再次發起該請求時,攜帶上次請求返回的Last-Modified值,經過此字段值告訴服務器該資源上次請求返回的最後被修改時間。服務器收到該請求,發現請求頭含有If-Modified-Since字段,則會根據If-Modified-Since的字段值與該資源在服務器的最後被修改時間作對比,若服務器的資源最後被修改時間大於If-Modified-Since的字段值,則從新返回資源,狀態碼爲200;不然則返回304,表明資源無更新,可繼續使用緩存文件,以下。
    image.png
  • Etag是服務器響應請求時,返回當前資源文件的一個惟一標識(由服務器生成),以下。
    image.png
  • If-None-Match是客戶端再次發起該請求時,攜帶上次請求返回的惟一標識Etag值,經過此字段值告訴服務器該資源上次請求返回的惟一標識值。服務器收到該請求後,發現該請求頭中含有If-None-Match,則會根據If-None-Match的字段值與該資源在服務器的Etag值作對比,一致則返回304,表明資源無更新,繼續使用緩存文件;不一致則從新返回資源文件,狀態碼爲200,以下。
    image.png
    ####總結 強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼表明該請求的緩存失效,從新獲取請求結果,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存,主要過程以下:
    image.png
    參考文檔:www.cnblogs.com/chenhuichao…

Node

1.理解Node在應用程序中的做用,可使用Node搭建前端運行環境、使用Node操做文件、操做數據庫等等

2.掌握一種Node開發框架,如Express,Express和Koa的區別

3.熟練使用Node提供的API如Path、Http、Child Process等並理解其實現原理

4.Node的底層運行原理、和瀏覽器的異同

5.Node事件驅動、非阻塞機制的實現原理

6、框架和類庫

TypeScript

1.理解泛型、接口等面向對象的相關概念,TypeScript對面向對象理念的實現

2.理解使用TypeScript的好處,掌握TypeScript基礎語法

3.TypeScript的規則檢測原理

4.能夠在React、Vue等框架中使用TypeScript進行開發

React

1.React和vue選型和優缺點、核心架構的區別

2.React中setState的執行機制,如何有效的管理狀態

3.React的事件底層實現機制

4.React的虛擬DOM和Diff算法的內部實現

5.React的Fiber工做原理,解決了什麼問題

6.React Router和Vue Router的底層實現原理、動態加載實現原理

7.可熟練應用React API、生命週期等,可應用HOC、render props、Hooks等高階用法解決問題

8.基於React的特性和原理,能夠手動實現一個簡單的React

Vue

1.熟練使用Vue的API、生命週期、鉤子函數

2.MVVM框架設計理念

3.Vue雙向綁定實現原理、Diff算法的內部實現

4.Vue的事件機制

5.從template轉換成真實DOM的實現機制

多端開發

1.單頁面應用(SPA)的原理和優缺點,掌握一種快速開發SPA的方案

2.理解Viewport、em、rem的原理和用法,分辨率、px、ppi、dpi、dp的區別和實際應用

3.移動端頁面適配解決方案、不一樣機型適配方案

4.掌握一種JavaScript移動客戶端開發技術,如React Native:能夠搭建React Native開發環境,熟練進行開發,可理解React Native的運做原理,不一樣端適配

5.掌握一種JavaScript PC客戶端開發技術,如Electron:可搭建Electron開發環境,熟練進行開發,可理解Electron的運做原理

6.掌握一種小程序開發框架或原生小程序開發

7.理解多端框架的內部實現原理,至少了解一個多端框架的使用

###數據流管理

1.掌握React和Vue傳統的跨組件通訊方案,對比採用數據流管理框架的異同

2.熟練使用Redux管理數據流,並理解其實現原理,中間件實現原理

3.熟練使用Mobx管理數據流,並理解其實現原理,相比Redux有什麼優點

4.熟練使用Vuex管理數據流,並理解其實現原理

5.以上數據流方案的異同和優缺點,不狀況下的技術選型

7、前端工程

項目構建

1.理解npm、yarn依賴包管理的原理,二者的區別

yarn是通過從新設計的嶄新的npm客戶端,它能讓開發人員並行處理全部必須的操做,並添加了一些其餘改進。 運行速度獲得了顯著的提高,整個安裝時間也變得更少 像npm同樣,yarn使用本地緩存。與npm不一樣的是,yarn無需互聯網鏈接就能安裝本地緩存的依賴項,它提供了離線模式。這個功能在2012年的npm項目中就被提出來過,但一直沒有實現。 容許合併項目中使用到的全部的包的許可證

2.可使用npm運行自定義腳本

3.理解Babel、ESLint、webpack等工具在項目中承擔的做用

4.ESLint規則檢測原理,經常使用的ESLint配置

5.Babel的核心原理,能夠本身編寫一個Babel插件

image.png

和編譯器相似,babel 的轉譯過程也分爲三個階段,這三步具體是:

1.解析 Parse 將代碼解析生成抽象語法樹( 即AST ),也就是計算機理解咱們代碼的方式(擴展:通常來講每一個 js 引擎都有本身的 AST,好比熟知的 v8,chrome 瀏覽器會把 js 源碼轉換爲抽象語法樹,再進一步轉換爲字節碼或機器代碼),而 babel 則是經過 babylon 實現的 。簡單來講就是一個對於 JS 代碼的一個編譯過程,進行了詞法分析與語法分析的過程。

2.轉換 Transform 對於 AST 進行變換一系列的操做,babel 接受獲得 AST 並經過 babel-traverse 對其進行遍歷,在此過程當中進行添加、更新及移除等操做。

3.生成 Generate 將變換後的 AST 再轉換爲 JS 代碼, 使用到的模塊是 babel-generator。 而 babel-core 模塊則是將三者結合使得對外提供的API作了一個簡化。

6.能夠配置一種前端代碼兼容方案,如Polyfill

7.Webpack的編譯原理、構建流程、熱更新原理,chunk、bundle和module的區別和應用

8.可熟練配置已有的loaders和plugins解決問題,能夠本身編寫loaders和plugins

nginx

1.正向代理與反向代理的特色和實例

2.可手動搭建一個簡單的nginx服務器、

3.熟練應用經常使用的nginx內置變量,掌握經常使用的匹配規則寫法

4.能夠用nginx實現請求過濾、配置gzip、負載均衡等,並能解釋其內部原理

8、項目和業務

性能優化

1.瞭解前端性能衡量指標、性能監控要點,掌握一種前端性能監控方案

2.瞭解常見的Web、App性能優化方案

3.SEO排名規則、SEO優化方案、先後端分離的SEO

4.SSR實現方案、優缺點、及其性能優化

5.Webpack的性能優化方案

6.Canvas性能優化方案

7.React、Vue等框架使用性能優化方案

前端安全

1.XSS攻擊的原理、分類、具體案例,前端如何防護

跨站腳本攻擊是指經過存在安全漏洞的Web網站註冊用戶的瀏覽器內運行非法的HTML標籤或JavaScript進行的一種攻擊。 XSS 的原理是惡意攻擊者往 Web 頁面裏插入惡意可執行網頁腳本代碼,當用戶瀏覽該頁之時,嵌入其中 Web 裏面的腳本代碼會被執行,從而能夠達到攻擊者盜取用戶信息或其餘侵犯用戶安全隱私的目的。

1.非持久型 XSS(反射型 XSS )

非持久型 XSS 漏洞攻擊有如下幾點特徵:

  • 即時性,不通過服務器存儲,直接經過 HTTP 的 GET 和 POST 請求就能完成一次攻擊,拿到用戶隱私數據。
  • 攻擊者須要誘騙點擊,必需要經過用戶點擊連接才能發起
  • 反饋率低,因此較難發現和響應修復
  • 盜取用戶敏感保密信息

爲了防止出現非持久型 XSS 漏洞,須要確保這麼幾件事情:

  • Web 頁面渲染的全部內容或者渲染的數據都必須來自於服務端。
  • 儘可能不要從 URL,document.referrer,document.forms 等這種 DOM API 中獲取數據直接渲染。
  • 儘可能不要使用 eval, new Function(),document.write(),document.writeln(),window.setInterval(),window.setTimeout(),innerHTML,document.createElement() 等可執行字符串的方法。 若是作不到以上幾點,也必須對涉及 DOM 渲染的方法傳入的字符串參數作 escape 轉義。 前端渲染的時候對任何的字段都須要作 escape 轉義編碼

2.持久型 XSS(存儲型 XSS) 持久型 XSS 漏洞,通常存在於 Form 表單提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,將內容經正常功能提交進入數據庫持久保存,當前端頁面得到後端從數據庫中讀出的注入代碼時,剛好將其渲染執行

攻擊成功須要同時知足如下幾個條件:

  • POST 請求提交表單後端沒作轉義直接入庫。
  • 後端從數據庫中取出數據沒作轉義直接輸出給前端。
  • 前端拿到後端數據沒作轉義直接渲染成 DOM。

持久型 XSS 有如下幾個特色:

  • 持久性,植入在數據庫中
  • 盜取用戶敏感私密信息
  • 危害面廣

如何防護 1) CSP CSP 本質上就是創建白名單,開發者明確告訴瀏覽器哪些外部資源能夠加載和執行。咱們只須要配置規則,如何攔截是由瀏覽器本身實現的。咱們能夠經過這種方式來儘可能減小 XSS 攻擊。

一般能夠經過兩種方式來開啓 CSP:

  • 設置 HTTP Header 中的 Content-Security-Policy
  • 設置 meta 標籤的方式

2) 轉義字符 用戶的輸入永遠不可信任的,最廣泛的作法就是轉義輸入輸出的內容,對於引號、尖括號、斜槓進行轉義

3) HttpOnly Cookie 這是預防XSS攻擊竊取用戶cookie最有效的防護手段。Web應用程序在設置cookie時,將其屬性設爲HttpOnly,就能夠避免該網頁的cookie被客戶端惡意JavaScript竊取,保護用戶cookie信息。

2.CSRF攻擊的原理、具體案例,前端如何防護

1.CSRF攻擊的原理 完成 CSRF 攻擊必需要有三個條件:

  • 用戶已經登陸了站點 A,並在本地記錄了 cookie
  • 在用戶沒有登出站點 A 的狀況下(也就是 cookie 生效的狀況下),訪問了惡意攻擊者提供的引誘危險站點 B (B 站點要求訪問站點A)。
  • 站點 A 沒有作任何 CSRF 防護

2.如何防護 防範 CSRF 攻擊能夠遵循如下幾種規則:

  • Get 請求不對數據進行修改
  • 不讓第三方網站訪問到用戶 Cookie
  • 阻止第三方網站請求接口
  • 請求時附帶驗證信息,好比驗證碼或者 Token 1) SameSite 能夠對 Cookie 設置 SameSite 屬性。該屬性表示 Cookie 不隨着跨域請求發送,能夠很大程度減小 CSRF 的攻擊,可是該屬性目前並非全部瀏覽器都兼容。

2) Referer Check HTTP Referer是header的一部分,當瀏覽器向web服務器發送請求時,通常會帶上Referer信息告訴服務器是從哪一個頁面連接過來的,服務器籍此能夠得到一些信息用於處理。能夠經過檢查請求的來源來防護CSRF攻擊。正常請求的referer具備必定規律,如在提交表單的referer一定是在該頁面發起的請求。因此經過檢查http包頭referer的值是否是這個頁面,來判斷是否是CSRF攻擊。

但在某些狀況下如從https跳轉到http,瀏覽器處於安全考慮,不會發送referer,服務器就沒法進行check了。若與該網站同域的其餘網站有XSS漏洞,那麼攻擊者能夠在其餘網站注入惡意腳本,受害者進入了此類同域的網址,也會遭受攻擊。出於以上緣由,沒法徹底依賴Referer Check做爲防護CSRF的主要手段。可是能夠經過Referer Check來監控CSRF攻擊的發生。

3) Anti CSRF Token 目前比較完善的解決方案是加入Anti-CSRF-Token。即發送請求時在HTTP 請求中以參數的形式加入一個隨機產生的token,並在服務器創建一個攔截器來驗證這個token。服務器讀取瀏覽器當前域cookie中這個token值,會進行校驗該請求當中的token和cookie當中的token值是否都存在且相等,才認爲這是合法的請求。不然認爲此次請求是違法的,拒絕該次服務。

這種方法相比Referer檢查要安全不少,token能夠在用戶登錄後產生並放於session或cookie中,而後在每次請求時服務器把token從session或cookie中拿出,與本次請求中的token 進行比對。因爲token的存在,攻擊者沒法再構造出一個完整的URL實施CSRF攻擊。但在處理多個頁面共存問題時,當某個頁面消耗掉token後,其餘頁面的表單保存的仍是被消耗掉的那個token,其餘頁面的表單提交時會出現token錯誤。

3.HTTP劫持、頁面劫持的原理、防護措施

點擊劫持的原理 用戶在登錄 A 網站的系統後,被攻擊者誘惑打開第三方網站,而第三方網站經過 iframe 引入了 A 網站的頁面內容,用戶在第三方網站中點擊某個按鈕(被裝飾的按鈕),其實是點擊了 A 網站的按鈕。

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

該響應頭有三個值可選,分別是

  • DENY,表示頁面不容許經過 iframe 的方式展現
  • SAMEORIGIN,表示頁面能夠在相同域名下經過 iframe 的方式展現
  • ALLOW-FROM,表示頁面能夠在指定來源的 iframe 中展現

2)JavaScript 防護 對於某些遠古瀏覽器來講,並不能支持上面的這種方式,那咱們只有經過 JS 的方式來防護點擊劫持了。

if(top.location != self.location){
    top.location = self.location;
}
複製代碼

9、資源推薦

語言基礎

計算機基礎

數據結構和算法

運行環境

框架和類庫

前端工程

項目和業務

學習提高

另外推薦我一直在關注的幾位大佬的我的博客:

技術以外

相關文章
相關標籤/搜索