前端開發(fā)面試比問基礎(chǔ)大全

本文將從以下十一個維度為讀者總結(jié)前端基礎(chǔ)知識


image.png

JS基礎(chǔ)

  1. 如何在ES5環(huán)境下實現(xiàn)let
    對于這個問題腰素,我們可以直接查看 babel 轉(zhuǎn)換前后的結(jié)果皮钠,看一下在循環(huán)中通過 let 定義的變量是如何解決變量提升的問題


    image.png

    babel在let定義的變量前加了道下劃線秩彤,避免在塊級作用域外訪問到該變量咧纠,除了對變量名的轉(zhuǎn)換钝吮,我們也可以通過自執(zhí)行函數(shù)來模擬塊級作用域

(function(){

  for(var i = 0; i < 5; i ++){

    console.log(i)  // 0 1 2 3 4

  }

})();



console.log(i)      // Uncaught ReferenceError: i is not defined
  1. 如何在ES5環(huán)境下實現(xiàn)const
    實現(xiàn)const的關(guān)鍵在于 Object.defineProperty() 這個API迷雪,這個API用于在一個對象上增加或修改屬性。通過配置屬性描述符垄潮,可以精確地控制屬性行為烹卒。 Object.defineProperty() 接收三個參數(shù):
Object.defineProperty(obj, prop, desc)
QQ截圖20200617133452.png

對于const不可修改的特性,我們通過設(shè)置writable屬性來實現(xiàn)

function _const(key, value) {    

    const desc = {        

        value,        

        writable: false    

    }    

    Object.defineProperty(window, key, desc)

}



_const('obj', {a: 1})   //定義obj

obj.b = 2               //可以正常給obj的屬性賦值

obj = {}                //拋出錯誤弯洗,提示對象read-only

參考資料: 如何在 ES5 環(huán)境下實現(xiàn)一個const 旅急? [3]

  1. 手寫call()
    call() 方法 使用一個指定的 this 值和單獨給出的一個或多個參數(shù)來調(diào)用一個函數(shù)

語法: function.call(thisArg, arg1, arg2, ...)

call() 的原理比較簡單,由于函數(shù)的this指向它的直接調(diào)用者牡整,我們變更調(diào)用者即完成this指向的變更:

/變更函數(shù)調(diào)用者示例

function foo() {

    console.log(this.name)

}



// 測試

const obj = {

    name: '寫代碼像蔡徐抻'

}

obj.foo = foo   // 變更foo的調(diào)用者

obj.foo()       // '寫代碼像蔡徐抻'

基于以上原理, 我們兩句代碼就能實現(xiàn)call()

Function.prototype.myCall = function(thisArg, ...args) {

    thisArg.fn = this              // this指向調(diào)用call的對象,即我們要改變this指向的函數(shù)

    return thisArg.fn(...args)     // 執(zhí)行函數(shù)并return其執(zhí)行結(jié)果

}

但是我們有一些細節(jié)需要處理:

Function.prototype.myCall = function(thisArg, ...args) {

    if(typeof this !== 'function') {

        throw new TypeError('error')

    }

    const fn = Symbol('fn')        // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性

    thisArg = thisArg || window    // 若沒有傳入this, 默認綁定window對象

    thisArg[fn] = this              // this指向調(diào)用call的對象,即我們要改變this指向的函數(shù)

    const result = thisArg[fn](...args)  // 執(zhí)行當前函數(shù)

    delete thisArg[fn]              // 刪除我們聲明的fn屬性

    return result                  // 返回函數(shù)執(zhí)行結(jié)果

}



//測試

foo.myCall(obj)     // 輸出'寫代碼像蔡徐抻'
  1. 手寫apply()
    apply() 方法調(diào)用一個具有給定this值的函數(shù)藐吮,以及作為一個數(shù)組(或類似數(shù)組對象)提供的參數(shù)。

語法:func.apply(thisArg, [argsArray])

apply() 和 call() 類似逃贝,區(qū)別在于call()接收參數(shù)列表谣辞,而apply()接收一個參數(shù)數(shù)組,所以我們在call()的實現(xiàn)上簡單改一下入?yún)⑿问郊纯?/p>

Function.prototype.myApply = function(thisArg, args) {

    if(typeof this !== 'function') {

        throw new TypeError('error')

    }

    const fn = Symbol('fn')        // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性

    thisArg = thisArg || window    // 若沒有傳入this, 默認綁定window對象

    thisArg[fn] = this              // this指向調(diào)用call的對象,即我們要改變this指向的函數(shù)

    const result = thisArg[fn](...args)  // 執(zhí)行當前函數(shù)

    delete thisArg[fn]              // 刪除我們聲明的fn屬性

    return result                  // 返回函數(shù)執(zhí)行結(jié)果

}



//測試

foo.myApply(obj, [])     // 輸出'寫代碼像蔡徐抻'
  1. 手寫bind()
    bind() 方法創(chuàng)建一個新的函數(shù)沐扳,在 bind() 被調(diào)用時潦闲,這個新函數(shù)的 this 被指定為 bind() 的第一個參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù)迫皱,供調(diào)用時使用。

語法: function.bind(thisArg, arg1, arg2, ...)

從用法上看,似乎給call/apply包一層function就實現(xiàn)了bind():

Function.prototype.myBind = function(thisArg, ...args) {

    return () => {

        this.apply(thisArg, args)

    }

}

但我們忽略了三點:

  1. bind()除了this還接收其他參數(shù)卓起,bind()返回的函數(shù)也接收參數(shù)和敬,這兩部分的參數(shù)都要傳給返回的函數(shù) 2. new的優(yōu)先級:如果bind綁定后的函數(shù)被new了,那么此時this指向就發(fā)生改變戏阅。此時的this就是當前函數(shù)的實例 3. 沒有保留原函數(shù)在原型鏈上的屬性和方法
Function.prototype.myBind = function (thisArg, ...args) {

    if (typeof this !== "function") {

      throw TypeError("Bind must be called on a function")

    }



    var self = this

    // new優(yōu)先級

    var fbound = function () {

        self.apply(this instanceof self ? this : thisArg, args.concat(Array.prototype.slice.call(arguments)))

    }

    // 繼承原型上的屬性和方法

    fbound.prototype = Object.create(self.prototype);



    return fbound;

}



//測試

const obj = { name: '寫代碼像蔡徐抻' }

function foo() {

    console.log(this.name)

    console.log(arguments)

}



foo.myBind(obj, 'a', 'b', 'c')()    //輸出寫代碼像蔡徐抻 ['a', 'b', 'c']
  1. 手寫一個防抖函數(shù)
    防抖和節(jié)流的概念都比較簡單昼弟,所以我們就不在“防抖節(jié)流是什么”這個問題上浪費過多篇幅了,簡單點一下:

防抖奕筐,即 短時間內(nèi)大量觸發(fā)同一事件舱痘,只會執(zhí)行一次函數(shù) ,實現(xiàn)原理為 設(shè)置一個定時器离赫,約定在xx毫秒后再觸發(fā)事件處理芭逝,每次觸發(fā)事件都會重新設(shè)置計時器,直到xx毫秒內(nèi)無第二次操作 渊胸,防抖常用于搜索框/滾動條的監(jiān)聽事件處理旬盯,如果不做防抖,每輸入一個字/滾動屏幕翎猛,都會觸發(fā)事件處理胖翰,造成性能浪費。

function debounce(func, wait) {

    let timeout = null

    return function() {

        let context = this

        let args = arguments

        if (timeout) clearTimeout(timeout)

        timeout = setTimeout(() => {

            func.apply(context, args)

        }, wait)

    }

}
  1. 手寫一個節(jié)流函數(shù)
    防抖是 延遲執(zhí)行 切厘,而節(jié)流是 間隔執(zhí)行 萨咳,函數(shù)節(jié)流即 每隔一段時間就執(zhí)行一次 ,實現(xiàn)原理為 設(shè)置一個定時器疫稿,約定xx毫秒后執(zhí)行事件培他,如果時間到了,那么執(zhí)行函數(shù)并重置定時器 而克,和防抖的區(qū)別在于靶壮,防抖每次觸發(fā)事件都重置定時器,而節(jié)流在定時器到時間后再清空定時器
function throttle(func, wait) {

    let timeout = null

    return function() {

        let context = this

        let args = arguments

        if (!timeout) {

            timeout = setTimeout(() => {

                timeout = null

                func.apply(context, args)

            }, wait)

        }
}
}

實現(xiàn)方式2:使用兩個時間戳prev舊時間戳now新時間戳员萍,每次觸發(fā)事件都判斷二者的時間差腾降,如果到達規(guī)定時間,執(zhí)行函數(shù)并重置舊時間戳


function throttle(func, wait) {

    var prev = 0;

    return function() {

        let now = Date.now();

        let context = this;

        let args = arguments;

        if (now - prev > wait) {

            func.apply(context, args);

            prev = now;

        }

    }

}
  1. 數(shù)組扁平化
    對于 [1, [1,2], [1,2,3]] 這樣多層嵌套的數(shù)組碎绎,我們?nèi)绾螌⑵浔馄交癁? [1, 1, 2, 1, 2, 3] 這樣的一維數(shù)組呢:

1.ES6的flat()

const arr = [1, [1,2], [1,2,3]]

arr.flat(Infinity)  // [1, 1, 2, 1, 2, 3]

2.序列化后正則

const arr = [1, [1,2], [1,2,3]]

const str = `[${JSON.stringify(arr).replace(/(\[|\])/g, '')}]`

JSON.parse(str)   // [1, 1, 2, 1, 2, 3]

3.遞歸

對于樹狀結(jié)構(gòu)的數(shù)據(jù)螃壤,最直接的處理方式就是遞歸

const arr = [1, [1,2], [1,2,3]]

function flat(arr) {

  let result = []

  for (const item of arr) {

    item instanceof Array ? result = result.concat(flat(item)) : result.push(item)

  }

  return result

}



flat(arr) // [1, 1, 2, 1, 2, 3]

4.reduce()遞歸

const arr = [1, [1,2], [1,2,3]]

function flat(arr) {

  return arr.reduce((prev, cur) => {

    return prev.concat(cur instanceof Array ? flat(cur) : cur)

  }, [])

}



flat(arr)  // [1, 1, 2, 1, 2, 3]

5.迭代+展開運算符

let arr = [1, [1,2], [1,2,3]]

while (arr.some(Array.isArray)) {

  arr = [].concat(...arr);

}



console.log(arr)  // [1, 1, 2, 1, 2, 3]
  1. 手寫一個Promise
    實現(xiàn)一個符合規(guī)范的Promise篇幅比較長,建議閱讀筆者上一篇文章: 異步編程二三事 | Promise/async/Generator實現(xiàn)原理解析 | 9k字 [4]

JS面向?qū)ο?br> 在JS中一切皆對象筋帖,但JS并不是一種真正的面向?qū)ο?OOP)的語言奸晴,因為它缺少 類(class) 的概念。雖然ES6引入了 class 和 extends 日麸,使我們能夠輕易地實現(xiàn)類和繼承寄啼。但JS并不存在真實的類逮光,JS的類是通過函數(shù)以及原型鏈機制模擬的,本小節(jié)的就來探究如何在ES5環(huán)境下利用函數(shù)和原型鏈實現(xiàn)JS面向?qū)ο蟮奶匦?/p>

在開始之前墩划,我們先回顧一下原型鏈的知識涕刚,后續(xù) new 和 繼承 等實現(xiàn)都是基于原型鏈機制。很多介紹原型鏈的資料都能寫上洋洋灑灑幾千字乙帮,但我覺得讀者們不需要把原型鏈想太復(fù)雜杜漠,容易把自己繞進去,其實在我看來察净,原型鏈的核心只需要記住三點:

  1. 每個對象都有 proto屬性 驾茴,該屬性指向其原型對象,在調(diào)用實例的方法和屬性時氢卡,如果在實例對象上找不到锈至,就會往原型對象上找 2. 構(gòu)造函數(shù)的 prototype屬性 也指向?qū)嵗脑蛯ο?3. 原型對象的 constructor屬性 指向構(gòu)造函數(shù)

    QQ截圖20200617134105.png

  2. 模擬實現(xiàn)new
    首先我們要知道 new 做了什么

  3. 創(chuàng)建一個新對象,并繼承其構(gòu)造函數(shù)的 prototype 异吻,這一步是為了繼承構(gòu)造函數(shù)原型上的屬性和方法 2. 執(zhí)行構(gòu)造函數(shù)裹赴,方法內(nèi)的 this 被指定為該新實例 ,這一步是為了執(zhí)行構(gòu)造函數(shù)內(nèi)的賦值操作 3. 返回新實例 (規(guī)范規(guī)定诀浪,如果構(gòu)造方法返回了一個對象棋返,那么返回該對象,否則返回第一步創(chuàng)建的新對象)

// new是關(guān)鍵字,這里我們用函數(shù)來模擬,new Foo(args) <=> myNew(Foo, args)

function myNew(foo, ...args) {

  // 創(chuàng)建新對象,并繼承構(gòu)造方法的prototype屬性, 這一步是為了把obj掛原型鏈上, 相當于obj.__proto__ = Foo.prototype

  let obj = Object.create(foo.prototype)  



  // 執(zhí)行構(gòu)造方法, 并為其綁定新this, 這一步是為了讓構(gòu)造方法能進行this.name = name之類的操作, args是構(gòu)造方法的入?yún)? 因為這里用myNew模擬, 所以入?yún)膍yNew傳入

  let result = foo.apply(obj, args)



  // 如果構(gòu)造方法已經(jīng)return了一個對象, 那么就返回該對象, 一般情況下雷猪,構(gòu)造方法不會返回新實例睛竣,但使用者可以選擇返回新實例來覆蓋new創(chuàng)建的對象 否則返回myNew創(chuàng)建的新對象

  return typeof result === 'object' && result !== null ? result : obj

}



function Foo(name) {

  this.name = name

}

const newObj = myNew(Foo, 'zhangsan')

console.log(newObj)                 // Foo {name: "zhangsan"}

console.log(newObj instanceof Foo)  // true
  1. ES5如何實現(xiàn)繼承
    說到繼承,最容易想到的是ES6的 extends 求摇,當然如果只回答這個肯定不合格射沟,我們要從函數(shù)和原型鏈的角度上實現(xiàn)繼承,下面我們一步步地与境、遞進地實現(xiàn)一個合格的繼承

一. 原型鏈繼承
原型鏈繼承的原理很簡單验夯,直接讓子類的原型對象指向父類實例,當子類實例找不到對應(yīng)的屬性和方法時摔刁,就會往它的原型對象挥转,也就是父類實例上找,從而實現(xiàn)對父類的屬性和方法的繼承

// 父類

function Parent() {

    this.name = '寫代碼像蔡徐抻'

}

// 父類的原型方法

Parent.prototype.getName = function() {

    return this.name

}

// 子類

function Child() {}



// 讓子類的原型對象指向父類實例, 這樣一來在Child實例中找不到的屬性和方法就會到原型對象(父類實例)上尋找

Child.prototype = new Parent()

Child.prototype.constructor = Child // 根據(jù)原型鏈的規(guī)則,順便綁定一下constructor, 這一步不影響繼承, 只是在用到constructor時會需要



// 然后Child實例就能訪問到父類及其原型上的name屬性和getName()方法

const child = new Child()

child.name          // '寫代碼像蔡徐抻'

child.getName()     // '寫代碼像蔡徐抻'

原型繼承的缺點:

  1. 由于所有Child實例原型都指向同一個Parent實例, 因此對某個Child實例的父類引用類型變量修改會影響所有的Child實例 2. 在創(chuàng)建子類實例時無法向父類構(gòu)造傳參, 即沒有實現(xiàn) super() 的功能
// 示例:

function Parent() {

 this.name = ['寫代碼像蔡徐抻'] 

}

Parent.prototype.getName = function() {

 return this.name

}

function Child() {}

Child.prototype = new Parent() Child.prototype.constructor = Child

// 測試 const child1 = new Child() const child2 = new Child() child1.name[0] = 'foo' console.log(child1.name) // ['foo'] console.log(child2.name) // ['foo'] (預(yù)期是['寫代碼像蔡徐抻'], 對child1.name的修改引起了所有child實例的變化)


### 二. 構(gòu)造函數(shù)繼承

構(gòu)造函數(shù)繼承共屈,即在子類的構(gòu)造函數(shù)中執(zhí)行父類的構(gòu)造函數(shù)绑谣,并為其綁定子類的`this`,讓父類的構(gòu)造函數(shù)把成員屬性和方法都掛到`子類的this`上去拗引,這樣既能避免實例之間共享一個原型實例借宵,又能向父類構(gòu)造方法傳參

```js

function Parent(name) {

    this.name = [name]

}

Parent.prototype.getName = function() {

    return this.name

}

function Child() {

    Parent.call(this, 'zhangsan')   // 執(zhí)行父類構(gòu)造方法并綁定子類的this, 使得父類中的屬性能夠賦到子類的this上

}



//測試

const child1 = new Child()

const child2 = new Child()

child1.name[0] = 'foo'

console.log(child1.name)          // ['foo']

console.log(child2.name)          // ['zhangsan']

child2.getName()                  // 報錯,找不到getName(), 構(gòu)造函數(shù)繼承的方式繼承不到父類原型上的屬性和方法

構(gòu)造函數(shù)繼承的缺點:

  1. 繼承不到父類原型上的屬性和方法

三. 組合式繼承
既然原型鏈繼承和構(gòu)造函數(shù)繼承各有互補的優(yōu)缺點, 那么我們?yōu)槭裁床唤M合起來使用呢, 所以就有了綜合二者的組合式繼承

function Parent(name) {

    this.name = [name]

}

Parent.prototype.getName = function() {

    return this.name

}

function Child() {

    // 構(gòu)造函數(shù)繼承

    Parent.call(this, 'zhangsan') 

}

//原型鏈繼承

Child.prototype = new Parent()

Child.prototype.constructor = Child



//測試

const child1 = new Child()

const child2 = new Child()

child1.name[0] = 'foo'

console.log(child1.name)          // ['foo']

console.log(child2.name)          // ['zhangsan']

child2.getName()                  // ['zhangsan']

組合式繼承的缺點:

  1. 每次創(chuàng)建子類實例都執(zhí)行了兩次構(gòu)造函數(shù)( Parent.call() 和 new Parent() ),雖然這并不影響對父類的繼承矾削,但子類創(chuàng)建實例時壤玫,原型中會存在兩份相同的屬性和方法豁护,這并不優(yōu)雅

四. 寄生式組合繼承
為了解決構(gòu)造函數(shù)被執(zhí)行兩次的問題, 我們將 指向父類實例 改為 指向父類原型 , 減去一次構(gòu)造函數(shù)的執(zhí)行

function Parent(name) {

    this.name = [name]

}

Parent.prototype.getName = function() {

    return this.name

}

function Child() {

    // 構(gòu)造函數(shù)繼承

    Parent.call(this, 'zhangsan') 

}

//原型鏈繼承

// Child.prototype = new Parent()

Child.prototype = Parent.prototype  //將`指向父類實例`改為`指向父類原型`

Child.prototype.constructor = Child



//測試

const child1 = new Child()

const child2 = new Child()

child1.name[0] = 'foo'

console.log(child1.name)          // ['foo']

console.log(child2.name)          // ['zhangsan']

child2.getName()                  // ['zhangsan']

但這種方式存在一個問題,由于子類原型和父類原型指向同一個對象垦细,我們對子類原型的操作會影響到父類原型择镇,例如給 Child.prototype 增加一個getName()方法,那么會導(dǎo)致 Parent.prototype 也增加或被覆蓋一個getName()方法括改,為了解決這個問題,我們給 Parent.prototype 做一個淺拷貝

function Parent(name) {

    this.name = [name]

}

Parent.prototype.getName = function() {

    return this.name

}

function Child() {

    // 構(gòu)造函數(shù)繼承

    Parent.call(this, 'zhangsan') 

}

//原型鏈繼承

// Child.prototype = new Parent()

Child.prototype = Object.create(Parent.prototype)  //將`指向父類實例`改為`指向父類原型`

Child.prototype.constructor = Child



//測試

const child = new Child()

const parent = new Parent()

child.getName()                  // ['zhangsan']

parent.getName()                 // 報錯, 找不到getName()

到這里我們就完成了ES5環(huán)境下的繼承的實現(xiàn)家坎,這種繼承方式稱為 寄生組合式繼承 嘱能,是目前最成熟的繼承方式,babel對ES6繼承的轉(zhuǎn)化也是使用了寄生組合式繼承

我們回顧一下實現(xiàn)過程:

一開始最容易想到的是 原型鏈繼承 虱疏,通過把子類實例的原型指向父類實例來繼承父類的屬性和方法惹骂,但原型鏈繼承的缺陷在于 對子類實例繼承的引用類型的修改會影響到所有的實例對象 以及 無法向父類的構(gòu)造方法傳參 。

因此我們引入了 構(gòu)造函數(shù)繼承 , 通過在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù)并傳入子類this來獲取父類的屬性和方法做瞪,但構(gòu)造函數(shù)繼承也存在缺陷对粪,構(gòu)造函數(shù)繼承 不能繼承到父類原型鏈上的屬性和方法 。

所以我們綜合了兩種繼承的優(yōu)點装蓬,提出了 組合式繼承 著拭,但組合式繼承也引入了新的問題,它 每次創(chuàng)建子類實例都執(zhí)行了兩次父類構(gòu)造方法 牍帚,我們通過將 子類原型指向父類實例 改為 子類原型指向父類原型的淺拷貝 來解決這一問題儡遮,也就是最終實現(xiàn) —— 寄生組合式繼承


QQ截圖20200617134402.png

V8引擎機制

  1. V8如何執(zhí)行一段JS代碼


    QQ截圖20200617134442.png
  2. 預(yù)解析 :檢查語法錯誤但不生成AST 2. 生成AST :經(jīng)過詞法/語法分析,生成抽象語法樹 3. 生成字節(jié)碼 :基線編譯器(Ignition)將AST轉(zhuǎn)換成字節(jié)碼 4. 生成機器碼 :優(yōu)化編譯器(Turbofan)將字節(jié)碼轉(zhuǎn)換成優(yōu)化過的機器碼暗赶,此外在逐行執(zhí)行字節(jié)碼的過程中鄙币,如果一段代碼經(jīng)常被執(zhí)行,那么V8會將這段代碼直接轉(zhuǎn)換成機器碼保存起來蹂随,下一次執(zhí)行就不必經(jīng)過字節(jié)碼十嘿,優(yōu)化了執(zhí)行速度

上面幾點只是V8執(zhí)行機制的極簡總結(jié),建議閱讀參考資料:

  1. V8 是怎么跑起來的 —— V8 的 JavaScript 執(zhí)行管道 [5]

JavaScript 引擎 V8 執(zhí)行流程概述 [6]

  1. 介紹一下引用計數(shù)和標記清除
    ? 引用計數(shù) :給一個變量賦值引用類型岳锁,則該對象的引用次數(shù)+1绩衷,如果這個變量變成了其他值,那么該對象的引用次數(shù)-1浸锨,垃圾回收器會回收引用次數(shù)為0的對象唇聘。但是當對象循環(huán)引用時,會導(dǎo)致引用次數(shù)永遠無法歸零柱搜,造成內(nèi)存無法釋放迟郎。 ? 標記清除 :垃圾收集器先給內(nèi)存中所有對象加上標記,然后從根節(jié)點開始遍歷聪蘸,去掉被引用的對象和運行環(huán)境中對象的標記宪肖,剩下的被標記的對象就是無法訪問的等待回收的對象表制。

  2. V8如何進行垃圾回收
    JS引擎中對變量的存儲主要有兩種位置,棧內(nèi)存和堆內(nèi)存控乾,棧內(nèi)存存儲基本類型數(shù)據(jù)以及引用類型數(shù)據(jù)的內(nèi)存地址么介,堆內(nèi)存儲存引用類型的數(shù)據(jù)


    QQ截圖20200617134509.png

    棧內(nèi)存的回收:

棧內(nèi)存調(diào)用棧上下文切換后就被回收,比較簡單

堆內(nèi)存的回收:

V8的堆內(nèi)存分為新生代內(nèi)存和老生代內(nèi)存蜕衡,新生代內(nèi)存是臨時分配的內(nèi)存壤短,存在時間短,老生代內(nèi)存存在時間長

? 新生代內(nèi)存回收機制:

? 新生代內(nèi)存容量小慨仿,64位系統(tǒng)下僅有32M久脯。新生代內(nèi)存分為 From、To 兩部分镰吆,進行垃圾回收時帘撰,先掃描From,將非存活對象回收万皿,將存活對象順序復(fù)制到To中摧找,之后調(diào)換From/To鞭执,等待下一次回收

? 老生代內(nèi)存回收機制

? 晉升 :如果新生代的變量經(jīng)過多次回收依然存在福贞,那么就會被放入老生代內(nèi)存中 ? 標記清除 :老生代內(nèi)存會先遍歷所有對象并打上標記瘟芝,然后對正在使用或被強引用的對象取消標記巍杈,回收被標記的對象 ? 整理內(nèi)存碎片 :把對象挪到內(nèi)存的一端

參考資料: 聊聊V8引擎的垃圾回收 [7]

  1. JS相較于C++等語言為什么慢付材,V8做了哪些優(yōu)化
  2. JS的問題:

? 動態(tài)類型 :導(dǎo)致每次存取屬性/尋求方法時候刁笙,都需要先檢查類型泻肯;此外動態(tài)類型也很難在編譯階段進行優(yōu)化 ? 屬性存取 :C++/Java等語言中方法海洼、屬性是存儲在數(shù)組中的佳励,僅需數(shù)組位移就可以獲取休里,而JS存儲在對象中,每次獲取都要進行哈希查詢

  1. V8的優(yōu)化:

? 優(yōu)化JIT(即時編譯) :相較于C++/Java這類編譯型語言赃承,JS一邊解釋一邊執(zhí)行妙黍,效率低。V8對這個過程進行了優(yōu)化:如果一段代碼被執(zhí)行多次瞧剖,那么V8會把這段代碼轉(zhuǎn)化為機器碼緩存下來拭嫁,下次運行時直接使用機器碼。 ? 隱藏類 :對于C++這類語言來說抓于,僅需幾個指令就能通過偏移量獲取變量信息做粤,而JS需要進行字符串匹配,效率低捉撮,V8借用了類和偏移位置的思想怕品,將對象劃分成不同的組,即隱藏類 ? 內(nèi)嵌緩存 :即緩存對象查詢的結(jié)果巾遭。常規(guī)查詢過程是:獲取隱藏類地址 -> 根據(jù)屬性名查找偏移值 -> 計算該屬性地址肉康,內(nèi)嵌緩存就是對這一過程結(jié)果的緩存 ? 垃圾回收管理 :上文已介紹


QQ截圖20200617134547.png

參考資料: 為什么V8引擎這么快闯估? [8]

瀏覽器渲染機制

  1. 瀏覽器的渲染過程是怎樣的


    QQ截圖20200617134615.png

    大體流程如下:

  2. HTML和CSS經(jīng)過各自解析,生成DOM樹和CSSOM樹 2. 合并成為渲染樹 3. 根據(jù)渲染樹進行布局 4. 最后調(diào)用GPU進行繪制吼和,顯示在屏幕上

  3. 如何根據(jù)瀏覽器渲染機制加快首屏速度

  4. 優(yōu)化文件大小 :HTML和CSS的加載和解析都會阻塞渲染樹的生成涨薪,從而影響首屏展示速度,因此我們可以通過優(yōu)化文件大小炫乓、減少CSS文件層級的方法來加快首屏速度 2. 避免資源下載阻塞文檔解析 :瀏覽器解析到<script>標簽時刚夺,會阻塞文檔解析,直到腳本執(zhí)行完成末捣,因此我們通常把<script>標簽放在底部光督,或者加上 defer、async 來進行異步下載

  5. 什么是回流(重排),什么情況下會觸發(fā)回流
    ? 當元素的尺寸或者位置發(fā)生了變化筐摘,就需要重新計算渲染樹卒茬,這就是回流 ? DOM元素的幾何屬性( width/height/padding/margin/border )發(fā)生變化時會觸發(fā)回流 ? DOM元素移動或增加會觸發(fā)回流 ? 讀寫 offset/scroll/client 等屬性時會觸發(fā)回流 ? 調(diào)用 window.getComputedStyle 會觸發(fā)回流

  6. 什么是重繪,什么情況下會觸發(fā)重繪
    ? DOM樣式發(fā)生了變化咖熟,但沒有影響DOM的幾何屬性時圃酵,會觸發(fā)重繪,而不會觸發(fā)回流馍管。重繪由于DOM位置信息不需要更新郭赐,省去了布局過程,因而性能上優(yōu)于回流

  7. 什么是GPU加速确沸,如何使用GPU加速捌锭,GPU加速的缺點
    ? 優(yōu)點 :使用transform、opacity罗捎、filters等屬性時观谦,會直接在GPU中完成處理,這些屬性的變化不會引起回流重繪 ? 缺點 :GPU渲染字體會導(dǎo)致字體模糊桨菜,過多的GPU處理會導(dǎo)致內(nèi)存問題

  8. 如何減少回流
    ? 使用 class 替代 style 豁状,減少style的使用 ? 使用 resize、scroll 時進行防抖和節(jié)流處理倒得,這兩者會直接導(dǎo)致回流 ? 使用 visibility 替換 display: none 泻红,因為前者只會引起重繪,后者會引發(fā)回流 ? 批量修改元素時霞掺,可以先讓元素脫離文檔流谊路,等修改完畢后,再放入文檔流 ? 避免觸發(fā)同步布局事件根悼,我們在獲取 offsetWidth 這類屬性的值時凶异,可以使用變量將查詢結(jié)果存起來蜀撑,避免多次查詢,每次對 offset/scroll/client 等屬性進行查詢時都會觸發(fā)回流 ? 對于復(fù)雜動畫效果,使用絕對定位讓其脫離文檔流剩彬,復(fù)雜的動畫效果會頻繁地觸發(fā)回流重繪酷麦,我們可以將動畫元素設(shè)置絕對定位從而脫離文檔流避免反復(fù)回流重繪。


    QQ截圖20200617134642.png

    考資料: 必須明白的瀏覽器渲染機制 [9]

瀏覽器緩存策略

  1. 介紹一下瀏覽器緩存位置和優(yōu)先級

  2. Service Worker 2. Memory Cache(內(nèi)存緩存) 3. Disk Cache(硬盤緩存) 4. Push Cache(推送緩存) 5. 以上緩存都沒命中就會進行網(wǎng)絡(luò)請求

  3. 說說不同緩存間的差別

  4. Service Worker

和Web Worker類似喉恋,是獨立的線程沃饶,我們可以在這個線程中緩存文件,在主線程需要的時候讀取這里的文件轻黑,Service Worker使我們可以自由選擇緩存哪些文件以及文件的匹配糊肤、讀取規(guī)則,并且緩存是持續(xù)性的

  1. Memory Cache

即內(nèi)存緩存氓鄙,內(nèi)存緩存不是持續(xù)性的馆揉,緩存會隨著進程釋放而釋放

  1. Disk Cache

即硬盤緩存,相較于內(nèi)存緩存抖拦,硬盤緩存的持續(xù)性和容量更優(yōu)升酣,它會根據(jù)HTTP header的字段判斷哪些資源需要緩存

  1. Push Cache

即推送緩存,是HTTP/2的內(nèi)容态罪,目前應(yīng)用較少

  1. 介紹一下瀏覽器緩存策略
    強緩存(不要向服務(wù)器詢問的緩存)

設(shè)置Expires
? 即過期時間噩茄,例如 「Expires: Thu, 26 Dec 2019 10:30:42 GMT」 表示緩存會在這個時間后失效,這個過期日期是絕對日期复颈,如果修改了本地日期绩聘,或者本地日期與服務(wù)器日期不一致,那么將導(dǎo)致緩存過期時間錯誤耗啦。

設(shè)置Cache-Control
? HTTP/1.1新增字段凿菩,Cache-Control可以通過 max-age 字段來設(shè)置過期時間,例如 「Cache-Control:max-age=3600」 除此之外Cache-Control還能設(shè)置 private/no-cache 等多種字段

協(xié)商緩存(需要向服務(wù)器詢問緩存是否已經(jīng)過期)

Last-Modified
? 即最后修改時間芹彬,瀏覽器第一次請求資源時蓄髓,服務(wù)器會在響應(yīng)頭上加上 Last-Modified ,當瀏覽器再次請求該資源時舒帮,瀏覽器會在請求頭中帶上 If-Modified-Since 字段会喝,字段的值就是之前服務(wù)器返回的最后修改時間,服務(wù)器對比這兩個時間玩郊,若相同則返回304肢执,否則返回新資源,并更新Last-Modified

ETag
? HTTP/1.1新增字段译红,表示文件唯一標識预茄,只要文件內(nèi)容改動,ETag就會重新計算。緩存流程和 Last-Modified 一樣:服務(wù)器發(fā)送 ETag 字段 -> 瀏覽器再次請求時發(fā)送 If-None-Match -> 如果ETag值不匹配耻陕,說明文件已經(jīng)改變拙徽,返回新資源并更新ETag,若匹配則返回304

兩者對比

? ETag 比 Last-Modified 更準確:如果我們打開文件但并沒有修改诗宣,Last-Modified 也會改變膘怕,并且 Last-Modified 的單位時間為一秒,如果一秒內(nèi)修改完了文件召庞,那么還是會命中緩存 ? 如果什么緩存策略都沒有設(shè)置岛心,那么瀏覽器會取響應(yīng)頭中的 Date 減去 Last-Modified 值的 10% 作為緩存時間


image.png

參考資料: 瀏覽器緩存機制剖析 [10]

網(wǎng)絡(luò)相關(guān)

  1. 講講網(wǎng)絡(luò)OSI七層模型,TCP/IP和HTTP分別位于哪一層


    image.png

    QQ截圖20200617134736.png
  2. 常見HTTP狀態(tài)碼有哪些
    2xx 開頭(請求成功)

200 OK :客戶端發(fā)送給服務(wù)器的請求被正常處理并返回

3xx 開頭(重定向)

301 Moved Permanently :永久重定向篮灼,請求的網(wǎng)頁已永久移動到新位置忘古。服務(wù)器返回此響應(yīng)時,會自動將請求者轉(zhuǎn)到新位置

302 Moved Permanently :臨時重定向诅诱,請求的網(wǎng)頁已臨時移動到新位置髓堪。服務(wù)器目前從不同位置的網(wǎng)頁響應(yīng)請求,但請求者應(yīng)繼續(xù)使用原有位置來進行以后的請求

304 Not Modified :未修改娘荡,自從上次請求后旦袋,請求的網(wǎng)頁未修改過。服務(wù)器返回此響應(yīng)時它改,不會返回網(wǎng)頁內(nèi)容

4xx 開頭(客戶端錯誤)

400 Bad Request :錯誤請求,服務(wù)器不理解請求的語法商乎,常見于客戶端傳參錯誤

401 Unauthorized :未授權(quán)央拖,表示發(fā)送的請求需要有通過 HTTP 認證的認證信息,常見于客戶端未登錄

403 Forbidden :禁止鹉戚,服務(wù)器拒絕請求鲜戒,常見于客戶端權(quán)限不足

404 Not Found :未找到,服務(wù)器找不到對應(yīng)資源

5xx 開頭(服務(wù)端錯誤)

500 Inter Server Error :服務(wù)器內(nèi)部錯誤抹凳,服務(wù)器遇到錯誤遏餐,無法完成請求

501 Not Implemented :尚未實施,服務(wù)器不具備完成請求的功能

502 Bad Gateway :作為網(wǎng)關(guān)或者代理工作的服務(wù)器嘗試執(zhí)行請求時赢底,從上游服務(wù)器接收到無效的響應(yīng)失都。

503 service unavailable :服務(wù)不可用,服務(wù)器目前無法使用(處于超載或停機維護狀態(tài))幸冻。通常是暫時狀態(tài)粹庞。

  1. GET請求和POST請求有何區(qū)別
    標準答案:

? GET請求參數(shù)放在URL上,POST請求參數(shù)放在請求體里 ? GET請求參數(shù)長度有限制洽损,POST請求參數(shù)長度可以非常大 ? POST請求相較于GET請求安全一點點庞溜,因為GET請求的參數(shù)在URL上,且有歷史記錄 ? GET請求能緩存碑定,POST不能

更進一步:

其實HTTP協(xié)議并沒有要求GET/POST請求參數(shù)必須放在URL上或請求體里流码,也沒有規(guī)定GET請求的長度又官,目前對URL的長度限制,是各家瀏覽器設(shè)置的限制漫试。GET和POST的根本區(qū)別在于: GET請求是冪等性的六敬,而POST請求不是

冪等性,指的是對某一資源進行一次或多次請求都具有相同的副作用商虐。例如搜索就是一個冪等的操作觉阅,而刪除、新增則不是一個冪等操作秘车。

由于GET請求是冪等的典勇,在網(wǎng)絡(luò)不好的環(huán)境中,GET請求可能會重復(fù)嘗試叮趴,造成重復(fù)操作數(shù)據(jù)的風(fēng)險割笙,因此,GET請求用于無副作用的操作(如搜索)眯亦,新增/刪除等操作適合用POST

參考資料: HTTP|GET 和 POST 區(qū)別伤溉?網(wǎng)上多數(shù)答案都是錯的 [11]

  1. HTTP的請求報文由哪幾部分組成
    一個HTTP請求報文由請求行(request line)、請求頭(header)妻率、空行和請求數(shù)據(jù)4個部分組成


    image.png

    響應(yīng)報文和請求報文結(jié)構(gòu)類似乱顾,不再贅述

  2. HTTP常見請求/響應(yīng)頭及其含義
    通用頭(請求頭和響應(yīng)頭都有的首部)


    QQ截圖20200617134842.png

    請求頭


    QQ截圖20200617134906.png

    響應(yīng)頭
    QQ截圖20200617134939.png

    響應(yīng)頭
    QQ截圖20200617135014.png

    實體頭(針對請求報文和響應(yīng)報文的實體部分使用首部)


    QQ截圖20200617135044.png

    HTTP首部當然不止這么幾個,但為了避免寫太多大家記不住( 主要是別的我也沒去看 )宫静,這里只介紹了一些常用的走净,詳細的可以看 MDN的文檔 [13]
  3. HTTP/1.0和HTTP/1.1有什么區(qū)別
    ? 長連接: HTTP/1.1支持長連接和請求的流水線,在一個TCP連接上可以傳送多個HTTP請求孤里,避免了因為多次建立TCP連接的時間消耗和延時 ? 緩存處理: HTTP/1.1引入 Entity tag伏伯,If-Unmodified-Since, If-Match, If-None-Match 等新的請求頭來控制緩存,詳見瀏覽器緩存小節(jié) ? 帶寬優(yōu)化及網(wǎng)絡(luò)連接的使用: HTTP1.1則在請求頭引入了range頭域捌袜,支持斷點續(xù)傳功能 ? Host頭處理: 在HTTP/1.0中認為每臺服務(wù)器都有唯一的IP地址说搅,但隨著虛擬主機技術(shù)的發(fā)展,多個主機共享一個IP地址愈發(fā)普遍虏等,HTTP1.1的請求消息和響應(yīng)消息都應(yīng)支持Host頭域弄唧,且請求消息中如果沒有Host頭域會400錯誤

  4. 介紹一下HTTP/2.0新特性
    ? 多路復(fù)用: 即多個請求都通過一個TCP連接并發(fā)地完成 ? 服務(wù)端推送: 服務(wù)端能夠主動把資源推送給客戶端 ? 新的二進制格式: HTTP/2采用二進制格式傳輸數(shù)據(jù),相比于HTTP/1.1的文本格式霍衫,二進制格式具有更好的解析性和拓展性 ? header壓縮: HTTP/2壓縮消息頭套才,減少了傳輸數(shù)據(jù)的大小

  5. 說說HTTP/2.0多路復(fù)用基本原理以及解決的問題
    HTTP/2解決的問題,就是HTTP/1.1存在的問題:

? TCP慢啟動: TCP連接建立后慕淡,會經(jīng)歷一個先慢后快的發(fā)送過程背伴,就像汽車啟動一般,如果我們的網(wǎng)頁文件(HTML/JS/CSS/icon)都經(jīng)過一次慢啟動,對性能是不小的損耗傻寂。另外慢啟動是TCP為了減少網(wǎng)絡(luò)擁塞的一種策略息尺,我們是沒有辦法改變的。 ? 多條TCP連接競爭帶寬: 如果同時建立多條TCP連接疾掰,當帶寬不足時就會競爭帶寬搂誉,影響關(guān)鍵資源的下載。 ? HTTP/1.1隊頭阻塞: 盡管HTTP/1.1長鏈接可以通過一個TCP連接傳輸多個請求静檬,但同一時刻只能處理一個請求炭懊,當前請求未結(jié)束前,其他請求只能處于阻塞狀態(tài)拂檩。

為了解決以上幾個問題侮腹, HTTP/2一個域名只使用一個TCP?連接來傳輸數(shù)據(jù),而且請求直接是并行的稻励、非阻塞的父阻,這就是多路復(fù)用

實現(xiàn)原理:HTTP/2引入了一個二進制分幀層,客戶端和服務(wù)端進行傳輸時望抽,數(shù)據(jù)會先經(jīng)過二進制分幀層處理加矛,轉(zhuǎn)化為一個個帶有請求ID的幀,這些幀在傳輸完成后根據(jù)ID組合成對應(yīng)的數(shù)據(jù)煤篙。

  1. 說說HTTP/3.0
    盡管HTTP/2解決了很多1.1的問題斟览,但HTTP/2仍然存在一些缺陷,這些缺陷并不是來自于HTTP/2協(xié)議本身辑奈,而是來源于底層的TCP協(xié)議趣惠,我們知道TCP鏈接是可靠的連接,如果出現(xiàn)了丟包身害,那么整個連接都要等待重傳,HTTP/1.1可以同時使用6個TCP連接草戈,一個阻塞另外五個還能工作塌鸯,但HTTP/2只有一個TCP連接,阻塞的問題便被放大了唐片。

由于TCP協(xié)議已經(jīng)被廣泛使用丙猬,我們很難直接修改TCP協(xié)議,基于此费韭,HTTP/3選擇了一個折衷的方法——UDP協(xié)議茧球,HTTP/2在UDP的基礎(chǔ)上實現(xiàn)多路復(fù)用、0-RTT星持、TLS加密抢埋、流量控制、丟包重傳等功能。

參考資料: http發(fā)展史(http0.9揪垄、http1.0穷吮、http1.1、http2饥努、http3)梳理筆記 [14] (推薦閱讀)

  1. HTTP和HTTPS有何區(qū)別
    ? HTTPS使用443端口捡鱼,而HTTP使用80 ? HTTPS需要申請證書 ? HTTP是超文本傳輸協(xié)議,是明文傳輸酷愧;HTTPS是經(jīng)過SSL加密的協(xié)議驾诈,傳輸更安全 ? HTTPS比HTTP慢,因為HTTPS除了TCP握手的三個包溶浴,還要加上SSL握手的九個包

  2. HTTPS是如何進行加密的
    我們通過分析幾種加密方式乍迄,層層遞進,理解HTTPS的加密方式以及為什么使用這種加密方式:

對稱加密

客戶端和服務(wù)器公用一個密匙用來對消息加解密戳葵,這種方式稱為對稱加密就乓。客戶端和服務(wù)器約定好一個加密的密匙拱烁∩希客戶端在發(fā)消息前用該密匙對消息加密,發(fā)送給服務(wù)器后戏自,服務(wù)器再用該密匙進行解密拿到消息邦投。
image.png

這種方式一定程度上保證了數(shù)據(jù)的安全性,但密鑰一旦泄露(密鑰在傳輸過程中被截獲)擅笔,傳輸內(nèi)容就會暴露志衣,因此我們要尋找一種安全傳遞密鑰的方法。

非對稱加密

采用非對稱加密時猛们,客戶端和服務(wù)端均擁有一個公鑰和私鑰念脯,公鑰加密的內(nèi)容只有對應(yīng)的私鑰能解密。私鑰自己留著弯淘,公鑰發(fā)給對方绿店。這樣在發(fā)送消息前,先用對方的公鑰對消息進行加密庐橙,收到后再用自己的私鑰進行解密假勿。這樣攻擊者只拿到傳輸過程中的公鑰也無法破解傳輸?shù)膬?nèi)容
image

盡管非對稱加密解決了由于密鑰被獲取而導(dǎo)致傳輸內(nèi)容泄露的問題,但中間人仍然可以用 篡改公鑰 的方式來獲取或篡改傳輸內(nèi)容态鳖,而且非對稱加密的性能比對稱加密的性能差了不少

image

第三方認證

上面這種方法的弱點在于转培,客戶端不知道公鑰是由服務(wù)端返回,還是中間人返回的浆竭,因此我們再引入一個第三方認證的環(huán)節(jié):即第三方使用私鑰加密我們 自己的公鑰 浸须,瀏覽器已經(jīng)內(nèi)置一些權(quán)威第三方認證機構(gòu)的公鑰惨寿,瀏覽器會使用 第三方的公鑰 來解開 第三方私鑰加密過的我們自己的公鑰,從而獲取公鑰羽戒,如果能成功解密缤沦,就說明獲取到的 自己的公鑰 是正確的

但第三方認證也未能完全解決問題,第三方認證是面向所有人的易稠,中間人也能申請證書缸废,如果中間人使用自己的證書掉包原證書,客戶端還是無法確認公鑰的真?zhèn)?/p>

image

數(shù)字簽名

為了讓客戶端能夠驗證公鑰的來源驶社,我們給公鑰加上一個數(shù)字簽名企量,這個數(shù)字簽名是由企業(yè)、網(wǎng)站等各種信息和公鑰經(jīng)過單向hash而來亡电,一旦構(gòu)成數(shù)字簽名的信息發(fā)生變化届巩,hash值就會改變,這就構(gòu)成了公鑰來源的唯一標識份乒。

具體來說恕汇,服務(wù)端本地生成一對密鑰,然后拿著公鑰以及企業(yè)或辖、網(wǎng)站等各種信息到CA(第三方認證中心)去申請數(shù)字證書瘾英,CA會通過一種單向hash算法(比如MD5),生成一串摘要颂暇,這串摘要就是這堆信息的唯一標識缺谴,然后CA還會使用自己的私鑰對摘要進行加密,連同我們自己服務(wù)器的公鑰一同發(fā)送給我我們耳鸯。

瀏覽器拿到數(shù)字簽名后,會使用 瀏覽器本地內(nèi)置 的CA公鑰解開數(shù)字證書并驗證察迟,從而拿到正確的公鑰乍赫。由于非對稱加密性能低下林束,拿到公鑰以后锨阿,客戶端會隨機生成一個對稱密鑰口渔,使用這個公鑰加密并發(fā)送給服務(wù)端知举,服務(wù)端用自己的私鑰解開對稱密鑰逛钻,此后的加密連接就通過這個對稱密鑰進行對稱加密。

綜上所述锰提,HTTPS在驗證階段使用非對稱加密+第三方認證+數(shù)字簽名獲取正確的公鑰曙痘,獲取到正確的公鑰后以對稱加密的方式通信

image

參考資料: 看圖學(xué)HTTPS [15]

前端安全

什么是CSRF攻擊

CSRF即Cross-site request forgery(跨站請求偽造),是一種挾制用戶在當前已登錄的Web應(yīng)用程序上執(zhí)行非本意的操作的攻擊方法欲账。

假如黑客在自己的站點上放置了其他網(wǎng)站的外鏈屡江,例如 "www.weibo.com/api ,默認情況下赛不,瀏覽器會帶著 weibo.com 的cookie訪問這個網(wǎng)址惩嘉,如果用戶已登錄過該網(wǎng)站且網(wǎng)站沒有對CSRF攻擊進行防御,那么服務(wù)器就會認為是用戶本人在調(diào)用此接口并執(zhí)行相關(guān)操作踢故,致使賬號被劫持文黎。

如何防御CSRF攻擊

? 驗證 Token :瀏覽器請求服務(wù)器時,服務(wù)器返回一個token殿较,每個請求都需要同時帶上token和cookie才會被認為是合法請求 ? 驗證 Referer :通過驗證請求頭的Referer來驗證來源站點耸峭,但請求頭很容易偽造 ? 設(shè)置 SameSite :設(shè)置cookie的SameSite,可以讓cookie不隨跨域請求發(fā)出淋纲,但瀏覽器兼容不一

什么是XSS攻擊

XSS即Cross Site Scripting(跨站腳本)劳闹,指的是通過利用網(wǎng)頁開發(fā)時留下的漏洞,注入惡意指令代碼到網(wǎng)頁洽瞬,使用戶加載并執(zhí)行攻擊者惡意制造的網(wǎng)頁程序本涕。常見的例如在評論區(qū)植入JS代碼疫衩,用戶進入評論頁時代碼被執(zhí)行鸠姨,造成頁面被植入廣告、賬號信息被竊取

XSS攻擊有哪些類型

? 存儲型 :即攻擊被存儲在服務(wù)端撞秋,常見的是在評論區(qū)插入攻擊腳本为障,如果腳本被儲存到服務(wù)端晦闰,那么所有看見對應(yīng)評論的用戶都會受到攻擊。 ? 反射型 :攻擊者將腳本混在URL里鳍怨,服務(wù)端接收到URL將惡意代碼當做參數(shù)取出并拼接在HTML里返回呻右,瀏覽器解析此HTML后即執(zhí)行惡意代碼 ? DOM型 :將攻擊腳本寫在URL中,誘導(dǎo)用戶點擊該URL鞋喇,如果URL被解析声滥,那么攻擊腳本就會被運行。和前兩者的差別主要在于DOM型攻擊不經(jīng)過服務(wù)端

如何防御XSS攻擊

? 輸入檢查 :對輸入內(nèi)容中的 <script><iframe> 等標簽進行轉(zhuǎn)義或者過濾 ? 設(shè)置httpOnly :很多XSS攻擊目標都是竊取用戶cookie偽造身份認證确徙,設(shè)置此屬性可防止JS獲取cookie ? 開啟CSP 醒串,即開啟白名單,可阻止白名單以外的資源加載和運行

image

排序算法

1. 手寫冒泡排序

冒泡排序應(yīng)該是很多人第一個接觸的排序鄙皇,比較簡單芜赌,不展開講解了

function bubbleSort(arr){

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

    for(let j = 0; j < arr.length - i - 1; j++) {

      if(arr[j] > arr[j+1]) {

        let temp = arr[j]

        arr[j] = arr[j+1]

        arr[j+1] = temp

      }

    }

  }

  return arr

}
  1. 如何優(yōu)化一個冒泡排序
    冒泡排序總會執(zhí)行(N-1)+(N-2)+(N-3)+..+2+1趟,但如果運行到當中某一趟時排序已經(jīng)完成伴逸,或者輸入的是一個有序數(shù)組缠沈,那么后邊的比較就都是多余的,為了避免這種情況错蝴,我們增加一個flag洲愤,判斷排序是否在中途就已經(jīng)完成(也就是判斷有無發(fā)生元素交換)
function bubbleSort(arr){

  let flag = true

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

    for(let j = 0; j < arr.length - i - 1; j++) {

      if(arr[j] > arr[j+1]) {

        flag = false

        let temp = arr[j]

        arr[j] = arr[j+1]

        arr[j+1] = temp

      }

    }

    if(flag)break;

  }

  return arr

}
  1. 手寫快速排序
    快排基本步驟:

  2. 選取基準元素 2. 比基準元素小的元素放到左邊,大的放右邊 3. 在左右子數(shù)組中重復(fù)步驟一二顷锰,直到數(shù)組只剩下一個元素 4. 向上逐級合并數(shù)組

function quickSort(arr) {

 if(arr.length <= 1) return arr          //遞歸終止條件

 const pivot = arr.length / 2 | 0        //基準點

 const pivotValue = arr.splice(pivot, 1)

 const leftArr = []

 const rightArr = []

 arr.forEach(val => {

     val > pivotValue ? rightArr.push(val) : leftArr.push(val)

 })

 return [ ...quickSort(leftArr), pivotValue, ...quickSort(rightArr)]

}

4. 如何優(yōu)化一個快速排序

原地排序

上邊這個快排只是讓讀者找找感覺柬赐,我們不能這樣寫快排,如果每次都開兩個數(shù)組官紫,會消耗很多內(nèi)存空間肛宋,數(shù)據(jù)量大時可能造成內(nèi)存溢出,我們要避免開新的內(nèi)存空間束世,即原地完成排序

我們可以用元素交換來取代開新數(shù)組酝陈,在每一次分區(qū)的時候直接在原數(shù)組上交換元素, 將小于基準數(shù)的元素挪到數(shù)組開頭 毁涉,以 [5,1,4,2,3] 為例:

image

我們定義一個pos指針, 標識等待置換的元素的位置, 然后逐一遍歷數(shù)組元素, 遇到比基準數(shù)小的就和arr[pos]交換位置, 然后pos++

代碼實現(xiàn):

function quickSort(arr, left, right) {          //這個left和right代表分區(qū)后“新數(shù)組”的區(qū)間下標沉帮,因為這里沒有新開數(shù)組,所以需要left/right來確認新數(shù)組的位置

    if (left < right) {

        let pos = left - 1                      //pos即“被置換的位置”贫堰,第一趟為-1

        for(let i = left; i <= right; i++) {    //循環(huán)遍歷數(shù)組穆壕,置換元素

            let pivot = arr[right]              //選取數(shù)組最后一位作為基準數(shù),

            if(arr[i] <= pivot) {               //若小于等于基準數(shù)严嗜,pos++粱檀,并置換元素, 這里使用小于等于而不是小于, 其實是為了避免因為重復(fù)數(shù)據(jù)而進入死循環(huán)

                pos++

                let temp = arr[pos]

                arr[pos] = arr[i]

                arr[i] = temp

            }

        }

        //一趟排序完成后,pos位置即基準數(shù)的位置漫玄,以pos的位置分割數(shù)組

        quickSort(arr, left, pos - 1)        

        quickSort(arr, pos + 1, right)

    }

    return arr      //數(shù)組只包含1或0個元素時(即left>=right)茄蚯,遞歸終止

}



//使用

var arr = [5,1,4,2,3]

var start = 0;

var end = arr.length - 1;

quickSort(arr, start, end)

這個交換的過程還是需要一些時間理解消化的,詳細分析可以看這篇: js算法-快速排序(Quicksort) [16]

三路快排

上邊這個快排還談不上優(yōu)化睦优,應(yīng)當說是快排的糾正寫法渗常,其實有兩個問題我們還能優(yōu)化一下:

  1. 有序數(shù)組的情況 :如果輸入的數(shù)組是有序的,而取基準點時也順序取汗盘,就可能導(dǎo)致基準點一側(cè)的子數(shù)組一直為空, 使時間復(fù)雜度退化到O(n2) 2. 大量重復(fù)數(shù)據(jù)的情況 :例如輸入的數(shù)據(jù)是[1,2,2,2,2,3] , 無論基準點取1皱碘、2還是3, 都會導(dǎo)致基準點兩側(cè)數(shù)組大小不平衡, 影響快排效率

對于第一個問題, 我們可以通過在取基準點的時候隨機化來解決,對于第二個問題隐孽,我們可以使用 三路快排 的方式來優(yōu)化癌椿,比方說對于上面的 [1,2,2,2,2,3] 健蕊,我們基準點取2,在分區(qū)的時候踢俄,將數(shù)組元素分為 小于2|等于2|大于2 三個區(qū)域缩功,其中等于基準點的部分不再進入下一次排序, 這樣就大大提高了快排效率

![https://upload-images.jianshu.io/upload_images/23679051-f44be68c8fcbfd8a.jpg!web?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

5. 手寫歸并排序

歸并排序和快排的思路類似,都是遞歸分治都办,區(qū)別在于快排邊分區(qū)邊排序嫡锌,而歸并在分區(qū)完成后才會排序

image
function mergeSort(arr) {

    if(arr.length <= 1) return arr        //數(shù)組元素被劃分到剩1個時,遞歸終止

    const midIndex = arr.length/2 | 0

    const leftArr = arr.slice(0, midIndex)

    const rightArr = arr.slice(midIndex, arr.length)

    return merge(mergeSort(leftArr), mergeSort(rightArr))    //先劃分琳钉,后合并

}



//合并

function merge(leftArr, rightArr) {

    const result = []

    while(leftArr.length && rightArr.length) {

        leftArr[0] <= rightArr[0] ? result.push(leftArr.shift()) : result.push(rightArr.shift())

    }

    while(leftArr.length) result.push(leftArr.shift())

    while(rightArr.length) result.push(rightArr.shift())

    return result

}

6. 手寫堆排序

堆是一棵特殊的樹, 只要滿足 這棵樹是完全二叉樹堆中每一個節(jié)點的值都大于或小于其左右孩子節(jié)點 這兩個條件, 那么就是一個堆, 根據(jù) 堆中每一個節(jié)點的值都大于或小于其左右孩子節(jié)點 , 又分為大根堆和小根堆

堆排序的流程:

  1. 初始化大(小)根堆势木,此時根節(jié)點為最大(小)值,將根節(jié)點與最后一個節(jié)點(數(shù)組最后一個元素)交換 2. 除開最后一個節(jié)點歌懒,重新調(diào)整大(小)根堆啦桌,使根節(jié)點為最大(小)值 3. 重復(fù)步驟二,直到堆中元素剩一個及皂,排序完成

[1,5,4,2,3] 為例構(gòu)筑大根堆:

image

代碼實現(xiàn):

// 堆排序

const heapSort = array => {

        // 我們用數(shù)組來儲存這個大根堆,數(shù)組就是堆本身

    // 初始化大頂堆震蒋,從第一個非葉子結(jié)點開始

    for (let i = Math.floor(array.length / 2 - 1); i >= 0; i--) {

        heapify(array, i, array.length);

    }

    // 排序,每一次 for 循環(huán)找出一個當前最大值躲庄,數(shù)組長度減一

    for (let i = Math.floor(array.length - 1); i > 0; i--) {

        // 根節(jié)點與最后一個節(jié)點交換

        swap(array, 0, i);

        // 從根節(jié)點開始調(diào)整查剖,并且最后一個結(jié)點已經(jīng)為當前最大值,不需要再參與比較噪窘,所以第三個參數(shù)為 i笋庄,即比較到最后一個結(jié)點前一個即可

        heapify(array, 0, i);

    }

    return array;

};



// 交換兩個節(jié)點

const swap = (array, i, j) => {

    let temp = array[i];

    array[i] = array[j];

    array[j] = temp;

};



// 將 i 結(jié)點以下的堆整理為大頂堆,注意這一步實現(xiàn)的基礎(chǔ)實際上是:

// 假設(shè)結(jié)點 i 以下的子堆已經(jīng)是一個大頂堆倔监,heapify 函數(shù)實現(xiàn)的

// 功能是實際上是:找到 結(jié)點 i 在包括結(jié)點 i 的堆中的正確位置直砂。

// 后面將寫一個 for 循環(huán),從第一個非葉子結(jié)點開始浩习,對每一個非葉子結(jié)點

// 都執(zhí)行 heapify 操作静暂,所以就滿足了結(jié)點 i 以下的子堆已經(jīng)是一大頂堆

const heapify = (array, i, length) => {

    let temp = array[i]; // 當前父節(jié)點

    // j < length 的目的是對結(jié)點 i 以下的結(jié)點全部做順序調(diào)整

    for (let j = 2 * i + 1; j < length; j = 2 * j + 1) {

        temp = array[i]; // 將 array[i] 取出,整個過程相當于找到 array[i] 應(yīng)處于的位置

        if (j + 1 < length && array[j] < array[j + 1]) {

            j++; // 找到兩個孩子中較大的一個谱秽,再與父節(jié)點比較

        }

        if (temp < array[j]) {

            swap(array, i, j); // 如果父節(jié)點小于子節(jié)點:交換洽蛀;否則跳出

            i = j; // 交換后,temp 的下標變?yōu)?j

        } else {

            break;

        }

    }

}

參考資料: JS實現(xiàn)堆排序 [17]

  1. 歸并疟赊、快排郊供、堆排有何區(qū)別
    QQ截圖20200617135428.png

    其實從表格中我們可以看到,就時間復(fù)雜度而言近哟,快排并沒有很大優(yōu)勢驮审,然而為什么快排會成為最常用的排序手段,這是因為時間復(fù)雜度只能說明 隨著數(shù)據(jù)量的增加,算法時間代價增長的趨勢疯淫,并不直接代表實際執(zhí)行時間地来,實際運行時間還包括了很多常數(shù)參數(shù)的差別,此外在面對不同類型數(shù)據(jù)(比如有序數(shù)據(jù)熙掺、大量重復(fù)數(shù)據(jù))時靠抑,表現(xiàn)也不同,綜合來說适掰,快排的時間效率是最高的

在實際運用中, 并不只使用一種排序手段, 例如V8的 Array.sort() 就采取了 當 n<=10 時, 采用插入排序, 當 n>10 時,采用三路快排 的排序策略

image

設(shè)計模式

設(shè)計模式有許多種荠列,這里挑出幾個常用的:| 設(shè)計模式 | 描述 | 例子 | | :-: | :-: | :-: | | 單例模式 | 一個類只能構(gòu)造出唯一實例 | Redux/Vuex的store | | 工廠模式 | 對創(chuàng)建對象邏輯的封裝 | jQuery的$(selector) | 觀察者模式 | 當一個對象被修改時类浪,會自動通知它的依賴對象 | Redux的subscribe、Vue的雙向綁定 | | 裝飾器模式 | 對類的包裝肌似,動態(tài)地拓展類的功能 | React高階組件费就、ES7 裝飾器 | 適配器模式 | 兼容新舊接口,對類的包裝 | 封裝舊API | 代理模式 | 控制對象的訪問 | 事件代理川队、ES6的Proxy

1. 介紹一下單一職責(zé)原則和開放封閉原則

?

單一職責(zé)原則:一個類只負責(zé)一個功能領(lǐng)域中的相應(yīng)職責(zé)力细,或者可以定義為:就一個類而言,應(yīng)該只有一個引起它變化的原因固额。

?

開放封閉原則:核心的思想是軟件實體(類眠蚂、模塊、函數(shù)等)是可擴展的斗躏、但不可修改的逝慧。也就是說,對擴展是開放的,而對修改是封閉的。

2. 單例模式

單例模式即一個類只能構(gòu)造出唯一實例啄糙,單例模式的意義在于 共享笛臣、唯一Redux/Vuex 中的store隧饼、 JQ 的$或者業(yè)務(wù)場景中的購物車沈堡、登錄框都是單例模式的應(yīng)用

class SingletonLogin {

  constructor(name,password){

    this.name = name

    this.password = password

  }

  static getInstance(name,password){

    //判斷對象是否已經(jīng)被創(chuàng)建,若創(chuàng)建則返回舊對象

    if(!this.instance)this.instance = new SingletonLogin(name,password)

    return this.instance

  }

}



let obj1 = SingletonLogin.getInstance('CXK','123')

let obj2 = SingletonLogin.getInstance('CXK','321')



console.log(obj1===obj2)    // true

console.log(obj1)           // {name:CXK,password:123}

console.log(obj2)           // 輸出的依然是{name:CXK,password:123}
  1. 工廠模式
    工廠模式即對創(chuàng)建對象邏輯的封裝,或者可以簡單理解為對 new 的封裝燕雁,這種封裝就像創(chuàng)建對象的工廠诞丽,故名工廠模式。工廠模式常見于大型項目拐格,比如JQ的對象率拒,我們創(chuàng)建選擇器對象時之所以沒有new selector就是因為()已經(jīng)是一個工廠方法,其他例子例如 React.createElement() 禁荒、 Vue.component() 都是工廠模式的實現(xiàn)猬膨。工廠模式有多種: 簡單工廠模式 、 工廠方法模式 、 抽象工廠模式 勃痴,這里只以簡單工廠模式為例:
class User {

  constructor(name, auth) {

    this.name = name

    this.auth = auth

  }

}



class UserFactory {

  static createUser(name, auth) {

    //工廠內(nèi)部封裝了創(chuàng)建對象的邏輯:權(quán)限為admin時,auth傳1,而使用者在外部創(chuàng)建對象時,不需要知道admin對應(yīng)哪個字段

    if(auth === 'admin')  new User(name, 1)

    if(auth === user)  new User(name, 2)

  }

}



const admin = UserFactory.createUser('admin');

const user = UserFactory.createUser('user');
  1. 觀察者模式
    觀察者模式算是前端最常用的設(shè)計模式了谒所,觀察者模式概念很簡單:觀察者監(jiān)聽被觀察者的變化,被觀察者發(fā)生改變時沛申,通知所有的觀察者劣领。觀察者模式被廣泛用于監(jiān)聽事件的實現(xiàn),有關(guān)觀察者模式的詳細應(yīng)用铁材,可以看我另一篇講解 Redux實現(xiàn)的文章 [18]
//觀察者

class Observer {    

  constructor (fn) {      

    this.update = fn    

  }

}

//被觀察者

class Subject {    

    constructor() {        

        this.observers = []          //觀察者隊列    

    }    

    addObserver(observer) {          

        this.observers.push(observer)//往觀察者隊列添加觀察者    

    }    

    notify() {                       //通知所有觀察者,實際上是把觀察者的update()都執(zhí)行了一遍       

        this.observers.forEach(observer => {        

            observer.update()            //依次取出觀察者,并執(zhí)行觀察者的update方法        

        })    

    }

}



var subject = new Subject()       //被觀察者

const update = () => {console.log('被觀察者發(fā)出通知')}  //收到廣播時要執(zhí)行的方法

var ob1 = new Observer(update)    //觀察者1

var ob2 = new Observer(update)    //觀察者2

subject.addObserver(ob1)          //觀察者1訂閱subject的通知

subject.addObserver(ob2)          //觀察者2訂閱subject的通知

subject.notify()                  //發(fā)出廣播,執(zhí)行所有觀察者的update方法

有些文章也把觀察者模式稱為發(fā)布訂閱模式尖淘,其實二者是有所區(qū)別的,發(fā)布訂閱相較于觀察者模式多一個調(diào)度中心著觉。

  1. 裝飾器模式
    裝飾器模式村生,可以理解為對類的一個包裝,動態(tài)地拓展類的功能饼丘,ES7的 裝飾器 語法以及React中的 高階組件 (HoC)都是這一模式的實現(xiàn)趁桃。react-redux的connect()也運用了裝飾器模式,這里以ES7的裝飾器為例:
function info(target) {

  target.prototype.name = '張三'

  target.prototype.age = 10

}



@info

class Man {}



let man = new Man()

man.name // 張三
  1. 適配器模式
    適配器模式肄鸽,將一個接口轉(zhuǎn)換成客戶希望的另一個接口卫病,使接口不兼容的那些類可以一起工作。我們在生活中就常常有使用適配器的場景典徘,例如出境旅游插頭插座不匹配蟀苛,這時我們就需要使用轉(zhuǎn)換插頭,也就是適配器來幫我們解決問題逮诲。
class Adaptee {

  test() {

      return '舊接口'

  }

}



class Target {

  constructor() {

      this.adaptee = new Adaptee()

  }

  test() {

      let info = this.adaptee.test()

      return `適配${info}`

  }

}



let target = new Target()

console.log(target.test())
  1. 代理模式
    代理模式屹逛,為一個對象找一個替代對象,以便對原對象進行訪問汛骂。即在訪問者與目標對象之間加一層代理罕模,通過代理做授權(quán)和控制。最常見的例子是經(jīng)紀人代理明星業(yè)務(wù)帘瞭,假設(shè)你作為一個投資者淑掌,想聯(lián)系明星打廣告,那么你就需要先經(jīng)過代理經(jīng)紀人蝶念,經(jīng)紀人對你的資質(zhì)進行考察抛腕,并通知你明星排期,替明星本人過濾不必要的信息媒殉。事件代理担敌、 JQuery的$.proxy 、ES6的 proxy 都是這一模式的實現(xiàn)廷蓉,下面以ES6的proxy為例:
const idol = {

  name: '蔡x抻',

  phone: 10086,

  price: 1000000  //報價

}



const agent = new Proxy(idol, {

  get: function(target) {

    //攔截明星電話的請求,只提供經(jīng)紀人電話

    return '經(jīng)紀人電話:10010'

  },

  set: function(target, key, value) {

    if(key === 'price' ) {

      //經(jīng)紀人過濾資質(zhì)

      if(value < target.price) throw new Error('報價過低')

      target.price = value

    }

  }

})





agent.phone        //經(jīng)紀人電話:10010

agent.price = 100  //Uncaught Error: 報價過低
image

HTML相關(guān)

1. 說說HTML5在標簽全封、屬性、存儲、API上的新特性

? 標簽:新增語義化標簽( aside / figure / section / header / footer / nav 等)刹悴,增加多媒體標簽 videoaudio 行楞,使得樣式和結(jié)構(gòu)更加分離 ? 屬性:增強表單,主要是增強了 input 的type屬性土匀; meta 增加charset以設(shè)置字符集子房; script 增加async以異步加載腳本 ? 存儲:增加 localStoragesessionStorageindexedDB 就轧,引入了 application cache 對web和應(yīng)用進行緩存 ? API:增加 拖放API 证杭、 地理定位SVG繪圖 妒御、 canvas繪圖 解愤、 Web WorkerWebSocket

2. doctype的作用是什么携丁?

聲明文檔類型,告知瀏覽器用什么文檔標準解析這個文檔:

? 怪異模式:瀏覽器使用自己的模式解析文檔兰怠,不加doctype時默認為怪異模式 ? 標準模式:瀏覽器以W3C的標準解析文檔

3. 幾種前端儲存以及它們之間的區(qū)別

? cookies :HTML5之前本地儲存的主要方式梦鉴,大小只有4k,HTTP請求頭會自動帶上cookie揭保,兼容性好 ? localStorage :HTML5新特性肥橙,持久性存儲,即使頁面關(guān)閉也不會被清除秸侣,以鍵值對的方式存儲存筏,大小為5M ? sessionStorage :HTML5新特性,操作及大小同localStorage味榛,和localStorage的區(qū)別在于sessionStorage在選項卡(頁面)被關(guān)閉時即清除椭坚,且不同選項卡之間的sessionStorage不互通 ? IndexedDB :NoSQL型數(shù)據(jù)庫,類比MongoDB搏色,使用鍵值對進行儲存善茎,異步操作數(shù)據(jù)庫,支持事務(wù)频轿,儲存空間可以在250MB以上垂涯,但是IndexedDB受同源策略限制 ? Web SQL:是在瀏覽器上模擬的關(guān)系型數(shù)據(jù)庫,開發(fā)者可以通過SQL語句來操作Web SQL航邢,是HTML5以外一套獨立的規(guī)范耕赘,兼容性差

4. href和src有什么區(qū)別

href(hyperReference) 即超文本引用:當瀏覽器遇到href時,會并行的地下載資源膳殷,不會阻塞頁面解析操骡,例如我們使用 <link> 引入CSS,瀏覽器會并行地下載CSS而不阻塞頁面解析. 因此我們在引入CSS時建議使用 <link> 而不是 @import

<link href="style.css" rel="stylesheet" />

src(resource)即資源,當瀏覽器遇到src時当娱,會暫停頁面解析吃既,直到該資源下載或執(zhí)行完畢,這也是script標簽之所以放底部的原因

<script src="script.js"></script>
  1. meta有哪些屬性跨细,作用是什么
    meta標簽用于描述網(wǎng)頁的 元信息 鹦倚,如網(wǎng)站作者、描述冀惭、關(guān)鍵詞震叙,meta通過name=xxxcontent=xxx的形式來定義信息,常用設(shè)置如下:

? charset:定義HTML文檔的字符集

<meta charset="UTF-8" >

http-equiv:可用于模擬http請求頭散休,可設(shè)置過期時間媒楼、緩存、刷新

<meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT">

? viewport:視口戚丸,用于控制頁面寬高及縮放比例

<meta 

    name="viewport" 

    content="width=device-width, initial-scale=1, maximum-scale=1"

>

. viewport有哪些參數(shù)划址,作用是什么

? width/height,寬高限府,默認寬度980px ? initial-scale夺颤,初始縮放比例,1~10 ? maximum-scale/minimum-scale胁勺,允許用戶縮放的最大/小比例 ? user-scalable世澜,用戶是否可以縮放 (yes/no)

7. http-equive屬性的作用和參數(shù)

? expires,指定過期時間 ? progma署穗,設(shè)置no-cache可以禁止緩存 ? refresh寥裂,定時刷新 ? set-cookie,可以設(shè)置cookie ? X-UA-Compatible案疲,使用瀏覽器版本 ? apple-mobile-web-app-status-bar-style封恰,針對WebApp全屏模式,隱藏狀態(tài)欄/設(shè)置狀態(tài)欄顏色

image

CSS相關(guān)

清除浮動的方法

為什么要清除浮動:清除浮動是為了解決子元素浮動而導(dǎo)致父元素高度塌陷的問題

image

1.添加新元素

<div class="parent">

  <div class="child"></div>

  <!-- 添加一個空元素褐啡,利用css提供的clear:both清除浮動 -->

  <div style="clear: both"></div>

</div>

2.使用偽元素

/* 對父元素添加偽元素 */

.parent::after{

  content: "";

  display: block;

  height: 0;

  clear:both;

}

3.觸發(fā)父元素BFC

/* 觸發(fā)父元素BFC */

.parent {

  overflow: hidden;

  /* float: left; */

  /* position: absolute; */

  /* display: inline-block */

  /* 以上屬性均可觸發(fā)BFC */

}

常見布局

編輯中俭驮,請稍等-_-||

什么是BFC

BFC全稱 Block Formatting Context 即 塊級格式上下文 ,簡單的說春贸,BFC是頁面上的一個隔離的獨立容器混萝,不受外界干擾或干擾外界

如何觸發(fā)BFC

? float 不為 none ? overflow 的值不為 visible ? position 為 absolute 或 fixed ? display 的值為 inline-block 或 table-cell 或 table-caption 或 grid

BFC的渲染規(guī)則是什么

? BFC是頁面上的一個隔離的獨立容器,不受外界干擾或干擾外界 ? 計算BFC的高度時萍恕,浮動子元素也參與計算(即內(nèi)部有浮動元素時也不會發(fā)生高度塌陷) ? BFC的區(qū)域不會與float的元素區(qū)域重疊 ? BFC內(nèi)部的元素會在垂直方向上放置 ? BFC內(nèi)部兩個相鄰元素的margin會發(fā)生重疊

BFC的應(yīng)用場景

? 清除浮動 :BFC內(nèi)部的浮動元素會參與高度計算逸嘀,因此可用于清除浮動,防止高度塌陷 ? 避免某元素被浮動元素覆蓋 :BFC的區(qū)域不會與浮動元素的區(qū)域重疊 ? 阻止外邊距重疊 :屬于同一個BFC的兩個相鄰Box的margin會發(fā)生折疊允粤,不同BFC不會發(fā)生折疊

image

總結(jié)

對于前端基礎(chǔ)知識的講解崭倘,到這里就告一小段落翼岁。前端的世界紛繁復(fù)雜,遠非筆者寥寥幾筆所能勾畫司光,筆者就像在沙灘上拾取貝殼的孩童琅坡,有時僥幸拾取收集一二,就為之歡欣鼓舞残家,迫不及待與伙伴們分享榆俺。

最后還想可恥地抒(自)發(fā)(夸)一下(? ̄?? ̄??)??°:

不知不覺,在掘金已經(jīng)水了半年有余坞淮,這半年來我寫下了近6萬字茴晋,不過其實一共只有5篇文章,這是因為我并不想寫水文回窘,不想把基礎(chǔ)的東西水上幾千字幾十篇來混贊升級诺擅。寫下的文章,首先要能說服自己啡直。要對自己寫下的東西負責(zé)任烁涌,即使是一張圖、一個標點酒觅。例如第一張圖撮执,我調(diào)整了不下十次,第一次我直接截取babel的轉(zhuǎn)化結(jié)果阐滩,覺得不好看二打,換成了代碼塊县忌,還是不好看掂榔,又換成了carbon的代碼圖,第一次下載症杏,發(fā)現(xiàn)兩張圖寬度不一樣装获,填充寬度重新下載,又發(fā)現(xiàn)自己的代碼少了一個空格厉颤,重新下載穴豫,為了實現(xiàn)兩張圖并排效果,寫了一個HTML來調(diào)整兩張圖的樣式逼友,為了保證每張圖的內(nèi)容和邊距一致精肃,我一邊截圖,一邊記錄下每次截圖的尺寸和邊距帜乞,每次截圖都根據(jù)上一次的數(shù)據(jù)調(diào)整邊距司抱。

其實我并非提倡把時間花在這些細枝末節(jié)上,只是單純覺得文章沒寫好黎烈,就不能發(fā)出來习柠,就像小野二郎先生說的那樣:“菜做的不好匀谣,就不能拿給客人吃”,世間的大道理资溃,往往都這樣通俗且簡潔武翎。
References
[1] 小冊: https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5c024ecbf265da616a476638

[2] 前端每日一問: https://github.com/sanyuan0704/sanyuan0704.github.io

[3] 如何在 ES5 環(huán)境下實現(xiàn)一個const ?: https://juejin.im/post/5ce3b2d451882533287a767f

[4] 異步編程二三事 | Promise/async/Generator實現(xiàn)原理解析 | 9k字: https://juejin.im/post/5e3b9ae26fb9a07ca714a5cc

[5] V8 是怎么跑起來的 —— V8 的 JavaScript 執(zhí)行管道: https://juejin.im/post/5dc4d823f265da4d4c202d3b

[6] JavaScript 引擎 V8 執(zhí)行流程概述: https://juejin.im/post/5df1ed1f6fb9a015fd69b78d

[7] 聊聊V8引擎的垃圾回收: https://juejin.im/post/5ad3f1156fb9a028b86e78be#heading-10

[8] 為什么V8引擎這么快溶锭?: https://zhuanlan.zhihu.com/p/27628685

[9] 必須明白的瀏覽器渲染機制: https://juejin.im/post/5ce120fbe51d4510a50334fa

[10] 瀏覽器緩存機制剖析: https://juejin.im/post/58eacff90ce4630058668257

[11] HTTP|GET 和 POST 區(qū)別宝恶?網(wǎng)上多數(shù)答案都是錯的: https://juejin.im/entry/597ca6caf265da3e301e64db

[12] www.baidu.com: http://www.baidu.com

[13] MDN的文檔: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

[14] http發(fā)展史(http0.9、http1.0暖途、http1.1卑惜、http2、http3)梳理筆記: https://juejin.im/post/5dbe8eba5188254fe019dabb#heading-9

[15] 看圖學(xué)HTTPS: https://juejin.im/post/5b0274ac6fb9a07aaa118f49#heading-5

[16] js算法-快速排序(Quicksort): https://segmentfault.com/a/1190000017814119

[17] JS實現(xiàn)堆排序: http://www.reibang.com/p/90bf2dcd6a7b

[18] Redux實現(xiàn)的文章: https://juejin.im/post/5def4831e51d45584b585000#heading-3
了方便進行探討和交流驻售,我為大家建立了一個Q群:1093606290露久,以方便大家學(xué)習(xí)交流。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欺栗,一起剝皮案震驚了整個濱河市毫痕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迟几,老刑警劉巖消请,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異类腮,居然都是意外死亡臊泰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門蚜枢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缸逃,“玉大人,你說我怎么就攤上這事厂抽⌒杵担” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵筷凤,是天一觀的道長昭殉。 經(jīng)常有香客問我,道長藐守,這世上最難降的妖魔是什么挪丢? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮卢厂,結(jié)果婚禮上乾蓬,老公的妹妹穿的比我還像新娘。我一直安慰自己足淆,他們只是感情好巢块,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布礁阁。 她就那樣靜靜地躺著,像睡著了一般族奢。 火紅的嫁衣襯著肌膚如雪姥闭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天越走,我揣著相機與錄音棚品,去河邊找鬼。 笑死廊敌,一個胖子當著我的面吹牛铜跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骡澈,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼锅纺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肋殴?” 一聲冷哼從身側(cè)響起囤锉,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎护锤,沒想到半個月后官地,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡烙懦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年驱入,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氯析。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡亏较,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魄鸦,到底是詐尸還是另有隱情宴杀,我是刑警寧澤癣朗,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布拾因,位于F島的核電站,受9級特大地震影響旷余,放射性物質(zhì)發(fā)生泄漏绢记。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一正卧、第九天 我趴在偏房一處隱蔽的房頂上張望蠢熄。 院中可真熱鬧,春花似錦炉旷、人聲如沸签孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饥追。三九已至图仓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間但绕,已是汗流浹背照激。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工陪白, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓嵌削,卻偏偏與公主長得像,于是被迫代替她去往敵國和親展哭。 傳聞我的和親對象是個殘疾皇子扳碍,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345