【前端面試匯總】

1. HTML

1. 必考:你是如何理解 HTML 語(yǔ)義化的?

荒野階段:最開(kāi)始是 PHP 后端寫(xiě) HTML,不會(huì) CSS,于是就用 table 來(lái)布局。table 使用展示表格的。嚴(yán)重違反了 HTML 語(yǔ)義化,索然很快就會(huì)寫(xiě)出頁(yè)面,但是后期維護(hù)很麻煩。
美工階段:后來(lái)有了專(zhuān)門(mén)的寫(xiě) CSS 、html 的前端,他們只會(huì)使用 DIV + CSS 布局,主要是用 float 和絕對(duì)定位布局。稍微符合一點(diǎn),但還是不夠語(yǔ)義化,因?yàn)椴恢烂總€(gè)div什么意思,其實(shí)是對(duì)于第一種方法的換湯不換藥的做法。
前端階段:再后來(lái),前端越來(lái)越專(zhuān)業(yè)化,知道 HTML 的各個(gè)標(biāo)簽的用法,于是會(huì)使用恰當(dāng)?shù)臉?biāo)簽來(lái)展示內(nèi)容,而不是傻傻的全用 div,會(huì)盡量使用標(biāo)題用 h1~h6、列表用ul/ol>li、段落用 p、導(dǎo)航欄用header, 側(cè)邊欄用aside,底部用footer,鏈接用a標(biāo)簽,圖片用img等等標(biāo)簽分別表示不同的意思。目前我們寫(xiě)頁(yè)面的就是html語(yǔ)義化標(biāo)簽。

語(yǔ)義化的好處是易讀、有利于SEO等。

2.meta viewport 是做什么用的,怎么寫(xiě)?

這個(gè)標(biāo)簽是控制頁(yè)面在移動(dòng)端不要縮小顯示。具體寫(xiě)法是<meta name=viewport content="width=divice-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
一開(kāi)始,所有頁(yè)面都是給PC準(zhǔn)備的,喬布斯推出 iPhone 3GS,頁(yè)面是不適應(yīng)手機(jī)屏幕的,所以喬布斯的工程師想了一個(gè)辦法,默認(rèn)把手機(jī)模擬成 980px,頁(yè)面縮小。
后來(lái),智能手機(jī)普及,這個(gè)功能在部分網(wǎng)站不需要了,所以我們就用 meta:vp 讓手機(jī)不要縮小我的網(wǎng)頁(yè)。

3.你用過(guò)哪些 HTML 5 標(biāo)簽?

最常見(jiàn)的有:

  • 內(nèi)容相關(guān)的:header/main/footer/article等

  • 功能相關(guān)的:canvas/video.audio等

     *   canvas繪制(的用法等):mdn,首先獲取canvas,然后獲取它的二維上下文,在設(shè)置筆的顏色最后設(shè)置筆刷的范圍,就能繪圖了。
     *   mdn,添加視頻  video標(biāo)簽  用到的屬性有:<video src="xx.ogg" autoplay poster="posterimg.jpg" >//想用自己喜歡的播放器看可以加a <a href="xxx.ogg">下載</a> //還可以加字幕:<track></vodeo>
    *    mdn, 添加音頻  audio   屬性和video的差不多  
    

4.H5 是什么?

移動(dòng)端頁(yè)面就叫做H5,注意注意!html5是一項(xiàng)標(biāo)準(zhǔn),而H5是是一門(mén)技術(shù),他倆不是一個(gè)概念。


2. CSS

1. 必考:兩種盒模型分別說(shuō)一下

  • border-box怪異(IE)和模型: width = padding + content + border
  • content-box標(biāo)準(zhǔn)盒模型:width = content
    高度和寬度類(lèi)似。
    兩種盒模型比較:正常情況下都用border-box,原因:border-box寫(xiě)起來(lái)更方便。

2. 必考:如何居中?

  1. 水平居中:
    • 塊級(jí)元素:
    • 內(nèi)聯(lián)元素:
  2. 垂直居中:

3. 考:flex 怎么用,常用屬性有哪些?

Flex是Flexible Box的縮寫(xiě),意為”彈性布局”,用來(lái)為盒狀模型提供最大的靈活性。Flex布局有兩層,采用flex布局的元素稱(chēng)為flex容器,其子元素則自動(dòng)成flex item,即項(xiàng)目.

  • flex容器屬性:

          flex-direction:決定項(xiàng)目的排列方向。
    
          flex-wrap:即一條軸線排不下時(shí)如何換行。
    
          flex-flow:是flex-direction屬性和flex-wrap屬性的簡(jiǎn)寫(xiě)形式,默認(rèn)值為row nowrap。
    
          justify-content:定義了項(xiàng)目在主軸上的對(duì)齊方式。(justify)
    
          align-items:定義項(xiàng)目在交叉軸上如何對(duì)齊。
    
          align-content:定義了多根軸線的對(duì)齊方式。如果項(xiàng)目只有一根軸線,該屬性不起作用。(換行會(huì)產(chǎn)生多軸)
    

flex-item屬性:

          order:定義項(xiàng)目的排列順序。數(shù)值越小,排列越靠前,默認(rèn)為0。
          flex-grow:定義項(xiàng)目的放大比例,如果所有項(xiàng)目的flex-grow屬性都為1,則它們將等分剩余空間(如果有的話)。如果一個(gè)項(xiàng)目的flex-grow屬性為2,其他項(xiàng)目都為1,則前者占據(jù)的剩余空間將比其他項(xiàng)多一倍。

          flex-shrink:定義了項(xiàng)目的縮小比例,默認(rèn)為1,如果所有項(xiàng)目的flex-shrink屬性都為1,當(dāng)空間不足時(shí),都將等比例縮小。如果一個(gè)項(xiàng)目的flex-shrink屬性為0,其他項(xiàng)目都為1,則空間不足時(shí),前者不縮小。

          flex-basis:定義了在分配多余空間之前,項(xiàng)目占據(jù)的主軸空間(main size)。

           flex:是flex-grow, flex-shrink 和 flex-basis的簡(jiǎn)寫(xiě),默認(rèn)值為0 1 auto。后兩個(gè)屬性可選。

          align-self:允許單個(gè)項(xiàng)目有與其他項(xiàng)目不一樣的對(duì)齊方式,可覆蓋align-items屬性。默認(rèn)值為auto,表示繼承父元素的align-items屬性,如果沒(méi)有父元素,則等同于stretch。

舉例子問(wèn)題:在行欄中,最左邊是一個(gè)按鈕,右邊有兩個(gè)按鈕
實(shí)現(xiàn)方法:

<box>
  <item>logo<item>
  <item>登錄<item>
  <item>注冊(cè)<item>
</box>
<style>
  box{
      display:flex;        
      justify-content:space-between;
}
item:nth-child(2){
    margin-left:auto;
}
</style>

4.必考:BFC 是什么?

中文翻譯:塊級(jí)格式化上下文,舉個(gè)例子:我如果給一個(gè)div添加overflow:hidden;那么它里面的浮動(dòng)元素就會(huì)被包裹起來(lái)。

  • 舉例(抽象問(wèn)題具體化:不能直接回答B(yǎng)FC是什么,而是舉含有BFC的例子):
    • overflow:hidden;是清楚浮動(dòng),但是不建議使用這個(gè)清楚浮動(dòng),而是用 .clearfix{}來(lái)清楚浮動(dòng)
    • overflow:hidden;消除父子margin 合并。

5. CSS 選擇器優(yōu)先級(jí)

選擇器優(yōu)先級(jí)的說(shuō)法 class第一位,id第二位,……是錯(cuò)的,在css2時(shí)帶還能用,但是目前css3時(shí)帶是不能用?!咀⒁狻咳绻鹔r引導(dǎo)你往這個(gè)錯(cuò)誤的方向,那就說(shuō)這個(gè)。

  1. 越具體優(yōu)先級(jí)越高
  2. 寫(xiě)在后面的覆蓋寫(xiě)在前面的
  3. !important的優(yōu)先級(jí)最高,但是建議少用  

6. 清除浮動(dòng)說(shuō)一下(背代碼)

  • .clearfix{
    content:'';
    display:block/table;
    clear:both;
    }
    把這個(gè)clearfix加到容器上,里面的子元素的浮動(dòng)就被清楚了

3.原生 js

1. 必考:ES 6 語(yǔ)法知道哪些,分別怎么用?

1. 作用域:

  • var 聲明的變量沒(méi)有塊級(jí)作用域m在語(yǔ)句塊里聲明的變量作用域是其所在的函數(shù)或者 script 標(biāo)簽內(nèi),你可以在語(yǔ)句塊外面訪問(wèn)到它。
  • let : 聲明塊級(jí)變量,是有塊級(jí)作用域的,而且不能重復(fù)聲明。在處理構(gòu)造函數(shù)的時(shí)候,可以通過(guò)let聲明而不是閉包來(lái)創(chuàng)建一個(gè)或多個(gè)私有成員。
  • const:聲明塊級(jí)變量,是有塊級(jí)作用域的,常量的值不能通過(guò)重新賦值來(lái)改變,并且不能重新聲明。

2. 箭頭函數(shù):

  • 箭頭函數(shù)的語(yǔ)法比函數(shù)表達(dá)式更簡(jiǎn)潔,沒(méi)有自己的this\arguments等,箭頭函數(shù)更適用于那些需要匿名函數(shù)的地方,并且不能用作構(gòu)造函數(shù)。箭頭函數(shù)只有IE瀏覽器不能兼容。
    1. sum = (a, b) => a + b
    2. nums.forEach( v => { console.log(v) })

3. 模板字符串:
1. 實(shí)現(xiàn)多行字符串:

```
    console.log(`string text line 1
    string text line 2`)
```
  1. 插入表達(dá)式:
       var a = 10;
       var b = 5;
       console.log(`Fifteen is ${a + b } and
       not ${a * b} `)
    

4. 解構(gòu)賦值:可以將屬性/值從對(duì)象/數(shù)組中取出,賦值給其他變量,對(duì)象或數(shù)組諸葛對(duì)應(yīng)值或表達(dá)式。

  • 結(jié)構(gòu)數(shù)組:
[a,b] = [10,20]
console.log(a); //10
console.log(b); //20
[a,b,...rest] = [10,20,30,40,50]
console.log(rest); //[30,40,50]
  • 交換變量:
var a = 10;
var b = 20;
[a,b] = [b,a];
  • 用正則表達(dá)式匹配提取值:用正則表達(dá)式的 exec()方法匹配字符串會(huì)返回一個(gè)數(shù)組,該數(shù)組第一個(gè)值是完全匹配正則表達(dá)式的字符串,然后的值是匹配正則表達(dá)式括號(hào)內(nèi)內(nèi)容部分。解構(gòu)賦值允許你輕易地提取出需要的部分,忽略完全匹配的字符串——如果不需要的話。
function parseProtocol(url) { 
  var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"]

  var [, protocol, fullhost, fullpath] = parsedURL;
  return protocol;
}

console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"
  • 結(jié)構(gòu)對(duì)象:
//基本賦值
var obj = {p:42,q:true}
var {p.q} = obj
console.log(p) //42
console.log(q) //true
//無(wú)聲明賦值
var a,b;
({a,b} = {a:1,b:2})
  • 對(duì)象解構(gòu)中的 Rest
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10 
b; // 20 
rest; // { c: 30, d: 40 }

5. 模塊
1.導(dǎo)入:import:

  • 靜態(tài)的inport語(yǔ)句帶入由另一個(gè)模板導(dǎo)出的綁定
//語(yǔ)法(必須在嚴(yán)格模式下,type='module'的script標(biāo)簽中使用)
import defaultExport from "Moudule-name" 
  • 類(lèi)似于函數(shù)的動(dòng)態(tài) import(),它不需要依賴(lài) type="module" 的script標(biāo)簽。
    script 標(biāo)簽中使用 nomodule 屬性,可以確保向后兼容
let module = await import('/modules/my-module.js')
  1. 導(dǎo)出:export:在創(chuàng)建 javascript 模塊時(shí),export語(yǔ)句從模塊中導(dǎo)出函數(shù)、對(duì)象或原始值,以便其他程序通過(guò)import 導(dǎo)入并使用他們。
//導(dǎo)出單個(gè)單個(gè)特性
export let name1,name2,...,nameN;(var const 一樣)
export function FunName(){}
export class ClassName{}
//導(dǎo)出列表
export {name1,name2,...,nameN}
//重命名導(dǎo)出
export {Variable1 as name1,variable2 as name2,...,namwN}
  1. 默認(rèn)導(dǎo)出:export default
export default expression
export default function(){}
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

4.模塊重定向

export {defaullt} from './other-module';
exprt * from './other-module';

6.class 類(lèi) :類(lèi)其實(shí)是一個(gè)特殊的函數(shù),類(lèi)語(yǔ)法糖有兩個(gè)部分組成:類(lèi)表達(dá)式和類(lèi)聲明

  • 類(lèi)聲明:不像函數(shù)聲明,類(lèi)聲明不會(huì)聲明提升,所以需要先聲明,否則返回一個(gè)ReferenceError
class Rectangle{
    constructor(width,height){
      this.width = width;
      this.height = height;
    }
}
  • 類(lèi)表達(dá)式:
//匿名類(lèi):
let Rectangle = class {
    constructor(width,height){
      this.width = width;
      this.height = height;
    }
}
//具名類(lèi)
let Rectangle = class  Rectangle{
    constructor(width,height){
      this.width = width;
      this.height = height;
    }
}
  • 使用extends創(chuàng)建子類(lèi)(注意:如果在子類(lèi)中使用構(gòu)造函數(shù)則在使用之前調(diào)用super())
class Animal{
    constructor(){
      this.name = name
    }
    speak(){
        console.log(this.name + 'makes a noise')
    }
}
class Dog extends Animal{
     speak(){
        console.log(this.name + 'barks')
      }
}
var dog  = new Dog('mike');
dog.speak();//mike barks
  • 也可以繼承傳統(tǒng)的基于函數(shù)的類(lèi)
function Animal(name){
    this.name = name
}
Animal.prototype.speak = function (){
    console.log(this.name + 'make a noise')
}
class Dog extends Animal{
    speak(){
        console.log(this.name + 'barks')
    }
}
var dog = new Dog('mike')
dog.speak();  //mike barks
  • 調(diào)用超類(lèi)
class Cat{
    constructor(name){
        this.name = name
    }

    speak(){
        console.log(this.name + 'makes a noise')
    }
}
 class Lion extends Cat{
    speak(){
        super.speak();
        console.log(this.name + 'roars')
    }
}

7.迭代器
for of VS for in :
這兩個(gè)語(yǔ)句都是迭代一些東西;
for in 以任意順序迭代對(duì)象的(synbol以外的)可枚舉屬性;
for of 遍歷可迭代對(duì)象要迭代的數(shù)據(jù)。

  • 一句話總結(jié):for of 遍歷的結(jié)果是值,for in 遍歷的結(jié)果是索引。
  • 舉例一:
let arr = ["Apple","Samsung","Nokia","Xiaomi"];

for (let value of arr){
   console.log(value)
}
//  Apple Samsung Nokia Xiaomi


for (let x in arr){
cosnole.log(x)
}
// 0 1 2 3
  • 舉例二:
let obj = { Apple:"iPhone X", Samsung:"Galaxy 6", Nokia:"Lumia",Xiaomi:"Note 3" }

for(let x of obj){
  console.log(x)
}

// !! 這樣就報(bào)錯(cuò)了(obj is not iterable)原因是這個(gè)對(duì)象的值的索引必須是可迭代的

for(let x in obj){
  console.log(x)
}

// Apple Samsung Nokia Xiaomi

  • 舉例三:
// for-of 遍歷可以迭代的任意對(duì)象。而 for-in 遍歷的是必需有明確索引的對(duì)象
function* company(){
  yield 'Apple';
  yield 'Samsung';
  yield 'Nokia';
  yield 'Xiaomi';
}


for(let x of company()){
  console.log(x)
}

// Apple Samsung Nokia Xiaomi

for(let x in company()){
  console.log(x)
}

// !! 這將什么都不會(huì)輸出
  • 總結(jié):for of 遍歷的結(jié)果是值,并且被遍歷的對(duì)象必須是可以迭代的,for in 遍歷的結(jié)果是索引,并且這個(gè)索引必須是明確的。

8.Symbol :
ES5的屬性名都是字符串,容易造成屬性名沖突,ES6引進(jìn)了一種新的原始數(shù)據(jù)類(lèi)型:Symbol,表示獨(dú)一無(wú)二值(每個(gè)從Symbol()返回的symbol值都是唯一的)。
Symbol函數(shù)可以接受一個(gè)字符串作為參數(shù),表示對(duì)Symbol實(shí)例的描述,主要是為了在控制臺(tái)顯示,或者轉(zhuǎn)為字符串時(shí),比較容易區(qū)分。
它每次都會(huì)創(chuàng)建一個(gè)新的 symbol類(lèi)型:

//沒(méi)有參數(shù)的情況
var s1 = Symbol();
var s2 = Symbol();
s1 === s2  //false
//有參數(shù)的情況下:
var s1 = Symbol('foo');
var s2 = Symbol('foo');
s1 === s2 //false

2. 必考 Promise、Promise.all、Promise.race 分別怎么用?

promise對(duì)象用于返回一個(gè)異步操作的最終完成結(jié)果。

  1. promise對(duì)象使用方法:

    • .then方法:
      $.ajax(……).then(成功函數(shù),失敗函數(shù))
    • 鏈?zhǔn)?.then方法:
      $.ajax(……).then(成功函數(shù),失敗函數(shù)).then(成功函數(shù)2,失敗函數(shù)2)
  2. 如何自己生成promise對(duì)象:

function fn(){
     return new Promise((resolve, reject)=>{
         成功時(shí)調(diào)用 resolve(數(shù)據(jù))
         失敗時(shí)調(diào)用 reject(錯(cuò)誤)
     })
 }
 fn().then(success, fail).then(success2, fail2)
  1. Promise.all()的用法:
    promise.all(iterable)方法返回一個(gè)promise實(shí)例,其參數(shù)iterable是可迭代對(duì)象,接受一個(gè)或多個(gè)值的數(shù)組(比如,立即值、promise、thenable)。它返回一個(gè)promise,此方法在集合多個(gè) promise 的返回結(jié)果時(shí)很有用。
    舉例:promise1和promise2都成功才會(huì)調(diào)用success1
          Promise.all([promise1, promise2]).then(success1, fail1)
  1. promise.race()的用法:顧名思義,Promise.race就是賽跑的意思,意思就是說(shuō),promise1和promise2只要有一個(gè)成功就會(huì)調(diào)用success1,不管結(jié)果本身是成功還是失敗狀態(tài)。所接受的參數(shù)是一樣的,接收多個(gè)值的數(shù)組。
 Promise.race([promise1, promise2]).then(success1, fail1)

3. 手寫(xiě)函數(shù)防抖和函數(shù)節(jié)流

  • 函數(shù)節(jié)流:可以理解為CD冷卻時(shí)間(就是在指定時(shí)間段內(nèi)不能執(zhí)行函數(shù)):
function fn(){}
CD = false
button.oncLick = function(){
    if(CD){
    }else{
      fn()
      CD = true
     var timerId = setTimeout(()=>{
        CD = false
      } ,3000)
    }
}
  • 防抖函數(shù):簡(jiǎn)單理解為把任務(wù)帶著一起做,比如說(shuō)我是一個(gè)送外賣(mài)的,然后規(guī)定如果定了一個(gè)外賣(mài)就再等5分鐘,如果來(lái)了繼續(xù)再等5分鐘,如果5分鐘之內(nèi)沒(méi)來(lái)過(guò)就開(kāi)始把積累的外賣(mài)都送。
    function fn(){}
    var timerId = null
    button.onclick = function(){
          if(timerId){
              window.clearTimeout(timerId)
          }
          fn()
         timerId = null
        timerId = setTimeout(()=>{
                  timerId = null
            },5000)
          }
    }

4. 必考:手寫(xiě)AJAX(背誦代碼)

function get/post(url,data,callback){
      var xhr = new XMLHttpRequest()
      xhr.open('POST',"/xxx")
      xhr.setRequestHeader("Content-type":"application/x-www-form-url-encoded")
      xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.response.Status === 200 || xhr.status === 304){
                  callback(response)
            }
     }
      xhr.send("a = 1 & b = 2")
}

5. 考:這段代碼里的 this 是什么?

函數(shù)里的this就看函數(shù)是怎么調(diào)用的:

  • fn() 在正常情況下:window;在'use strict'下:undefined
  • obj.fn() this =>obj
  • fn.call(xx) this=>xx
  • fn.appy(xx) this => xx
  • fn.bind(xx) this=> xx
  • new Fn() this=>新的對(duì)象(生成的實(shí)例)
  • fn = () => {} this=>外面的的this

6, 必考:閉包/立即執(zhí)行函數(shù)是什么?

  1. 閉包是什么?
    由于在JS中,變量的作用域?qū)儆诤瘮?shù)作用域,在函數(shù)執(zhí)行后作用域就會(huì)被清理、內(nèi)存也隨之回收,但是由于閉包是建立在一個(gè)函數(shù)內(nèi)部的子函數(shù),由于其可訪問(wèn)上級(jí)作用域的原因,即使上級(jí)函數(shù)執(zhí)行完,作用域也不會(huì)隨之銷(xiāo)毀,這時(shí)的子函數(shù)——也就是閉包,便擁有了訪問(wèn)上級(jí)作用域中的變量的權(quán)限,即使上級(jí)函數(shù)執(zhí)行完后作用域內(nèi)的值也不會(huì)被銷(xiāo)毀。
//這里就有閉包,local 變量和 bar 函數(shù)就組成了一個(gè)閉包(Closure)。
function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar //只是為了 bar 能被使用,也跟閉包無(wú)關(guān),把 return bar 改成 window.bar = bar 也是一樣的,只要讓外面可以訪問(wèn)到這個(gè) bar 函數(shù)就行了
}

var func = foo()
func()
  1. 閉包的作用:

在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁,由于閉包可以緩存上級(jí)作用域,那么就使得函數(shù)外部打破了“函數(shù)作用域”的束縛,可以訪問(wèn)函數(shù)內(nèi)部的變量。以平時(shí)使用的Ajax成功回調(diào)為例,這里其實(shí)就是個(gè)閉包,由于上述的特性,回調(diào)就擁有了整個(gè)上級(jí)作用域的訪問(wèn)和操作能力,提高了極大的便利。開(kāi)發(fā)者不用去寫(xiě)鉤子函數(shù)來(lái)操作上級(jí)函數(shù)作用域內(nèi)部的變量了。閉包隨處可見(jiàn),一個(gè)Ajax請(qǐng)求的成功回調(diào),一個(gè)事件綁定的回調(diào)方法,一個(gè)setTimeout的延時(shí)回調(diào),或者一個(gè)函數(shù)內(nèi)部返回另一個(gè)匿名函數(shù),這些都是閉包。簡(jiǎn)而言之,無(wú)論使用何種方式對(duì)函數(shù)類(lèi)型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都有閉包的身影。

7. 必考:什么是 JSONP,什么是 CORS,什么是跨域?

1. JSONP:
JSONP是JSON with Padding的略稱(chēng)。它是一個(gè)非官方的協(xié)議,它允許在服務(wù)器集成JavaScript返回至 客戶(hù)端,通過(guò)javascript callback形式實(shí)現(xiàn)跨域訪問(wèn)。

  • JSONP的完整過(guò)程:

    1. 請(qǐng)求方:一個(gè)網(wǎng)站的前端(瀏覽器):創(chuàng)建一個(gè)script,src指向響應(yīng)方,同時(shí)傳一個(gè)查詢(xún)參數(shù)callback=xxx;不用callbackName,而是用callback。
    2. 響應(yīng)方:另一個(gè)網(wǎng)站的后端(服務(wù)器),根據(jù)查詢(xún)參數(shù)callbackName,構(gòu)造形如xxx.call(undefined,'你要的參數(shù)')響應(yīng)。
    3. 瀏覽器接收到響應(yīng)就會(huì)執(zhí)行回調(diào)函數(shù)xxx.call(undefined,'你要的參數(shù)')。
    4. 請(qǐng)求方就會(huì)知道了他想要的數(shù)據(jù)(執(zhí)行函數(shù)體里面的執(zhí)行語(yǔ)句)。
      這就是JSONP
  • 行業(yè)約定:

    1. 查詢(xún)參數(shù)要用callback來(lái)指定,在jQuery中用jQuery_callback。
    2. 回調(diào)函數(shù)名不固定,就用用隨機(jī)函數(shù)名,簡(jiǎn)單又避免函數(shù)名重復(fù)的問(wèn)題,例如不叫xxx ,而是jQuery12343這種隨機(jī)數(shù);只要符合變量名規(guī)則就行。
    3. 調(diào)用函數(shù)后馬上刪除,避免污染全局變量。
  • 為什么SJONP只能發(fā)起不能發(fā)post請(qǐng)求?
    因?yàn)镴SONP是通過(guò)動(dòng)態(tài)創(chuàng)建scriipt實(shí)現(xiàn)的,動(dòng)態(tài)創(chuàng)建的script的時(shí)候只能發(fā)送get請(qǐng)求,不能發(fā)送post請(qǐng)求.

  • jQuery實(shí)現(xiàn)Ajax:

$.ajax({
    url : "http://jack.com:8080/pay",
    dataType : "jsonp",
     success : function(response){
        if(response === "success"){
             ...  //執(zhí)行語(yǔ)句
        }
    }
})

2. CORS:跨源資源分享
Cross-Origin Resource Sharing,是W3C標(biāo)準(zhǔn),它是解決Ajax跨源請(qǐng)求的根本方法,CORS 允許發(fā)送任何類(lèi)型的請(qǐng)求
用法:

  response.setHeader('Access-Control-Allow-Origin':'http://haha.com:8080')
//設(shè)置CORS,http://haha.com:8001這個(gè)網(wǎng)站可以訪問(wèn)本服務(wù)器響應(yīng)內(nèi)容

后臺(tái)用 JSONP 還是 CORS? 首先JSONP 不能發(fā)起POST請(qǐng)求但可以實(shí)現(xiàn)跨源請(qǐng)求。CORS可以發(fā)起多種類(lèi)型的請(qǐng)求(后臺(tái)必須要用POST請(qǐng)求的話只能用CORS),但是需要訪問(wèn)的話在后臺(tái)設(shè)置CORS才能實(shí)現(xiàn)跨源請(qǐng)求。
3. 跨域:
跨域是指從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源。比如從http://www.baidu.com/ 頁(yè)面去請(qǐng)求 http://www.google.com 的資源??缬虻膰?yán)格一點(diǎn)的定義是:只要 協(xié)議,域名,端口有任何一個(gè)的不同,就被當(dāng)作是跨域。

8. ??迹篴sync/await 怎么用,如何捕獲異常?

捕獲異常就是拋出promise.reject異常值

aysnc function fn(){
    try{
        var z = await.promise.reject(3)
    }catch(e){
        console.log(e)  //3
    }
}
fn();

9.常考:如何實(shí)現(xiàn)深拷貝?(背代碼)

  1. JSON來(lái)實(shí)現(xiàn)深拷貝:
      var a = {...}
      var b = JSON.parse( JSON.stringify(a) )
    

缺點(diǎn):JSON 不支持函數(shù)、引用、undefined、RegExp、Date……

  1. 遞歸:
       function clone(object){
          var object2
          if(! (object instanceof Object) ){
              return object
          }else if(object instanceof Array){
                object2 = []
          }else if(object instanceof Function){
                object2 = eval(object.toString())
          }else if(object instanceof Object){
                object2 = {}
          }
        //你也可以把 Array Function Object 都當(dāng)做 Object 來(lái)看待,參考 https://juejin.im/post/587dab348d6d810058d87a0a
          for(let key in object){
                object2[key] = clone(object[key])
          }
          return object2
      }
  1. 判斷類(lèi)型:

  2. 檢查循環(huán)引用(環(huán)):

10. 常考:如何用正則實(shí)現(xiàn) trim()?
      function trim(string){
            return string.replace(/^\s | \s+$/g,' ')
      }

11. 考:不用 class 如何實(shí)現(xiàn)繼承?用 class 又如何實(shí)現(xiàn)?

  • 不用class實(shí)現(xiàn)繼承:原型鏈
    function Animal(){
      this.body = '肉體'
    }
    Animal.prototype.move = function(){
    
    }
    
    function Human(name){
      Animal.apply(this, arguments)
      this.name = name
    }
    // Human.prototype.__proto__ = Animal.prototype // 非法
    
    var f = function(){}
    f.prototype = Animal.prototype
    Human.prototype = new f()
    
    Human.prototype.useTools = function(){}
    
    var qinglin = new Human()
    
    
  • 使用class 實(shí)現(xiàn)繼承:
     class Animal{
        constructor(){
          this.body = '肉體'
        },
        move(){}
    }
    class Dog extends Animal{
      constructor(name){
          super(name)
          this.name = name
      },
      useTools(){}
    }
    var qinglin = new Human()

12. ??迹喝绾螌?shí)現(xiàn)數(shù)組去重?

  • 第一種方案:
    //計(jì)數(shù)排序的邏輯(只能正整數(shù))
 var a = [4,2,5,6,3,4,5]
 var hashTab = {}
 for(let i=0; i<a.length;i++){
     if(a[i] in hashTab){
         // 什么也不做
     }else{
         hashTab[ a[i] ] = true
     }
 }
 //hashTab: {4: true, 2: true, 5: true, 6:true, 3: true}
 console.log(Object.keys(hashTab)) // ['4','2','5','6','3']

  • 第二種方案:
      function unique(array){
          return [...new set(array)]
      }

13. 棄:== 相關(guān)題目(反著答)

14. 送命題:手寫(xiě)一個(gè) Promise

function Promise(executor) {
    let self = this;
    self.status = 'pending'; //等待態(tài)
    self.value = undefined;  //成功的返回值
    self.reason = undefined; //失敗的原因

    function resolve(value){
        if(self.status === 'pending'){
            self.status = 'resolved';
            self.value = value;
        }
    }
    function reject(reason) {
        if(self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    try{
        executor(resolve, reject);
    }catch(e){
        reject(e);// 捕獲時(shí)發(fā)生異常,就直接失敗
    }
}
//onFufiled 成功的回調(diào)
//onRejected 失敗的回調(diào)
Promise.prototype.then = function (onFufiled, onRejected) {
    let self = this;
    if(self.status === 'resolved'){
        onFufiled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
}
module.exports = Promise;


4. Dom

1. 考:事件委托

//錯(cuò)誤版(但是可能能過(guò))bug 在于,如果用戶(hù)點(diǎn)擊的是 li 里面的 span,就沒(méi)法觸發(fā) fn,這顯然不對(duì)。
ul.addEventListener('click',function(e){
    if(e.target.tagName.tiLowerCase === 'li'){
        console.log('li被點(diǎn)擊了')
        fn() //執(zhí)行謀個(gè)函數(shù)
    }
})

最標(biāo)準(zhǔn)版:

function delegate(element, eventType, selector, fn) {
     element.addEventListener(eventType, e => {
       let el = e.target
       while (!el.matches(selector)) {
         if (element === el) {
           el = null
           break
         }
         el = el.parentNode
       }
       el && fn.call(el, e, el)
     })
     return element
   }

2. 曾考:用 mouse 事件寫(xiě)一個(gè)可拖曳的 div

//html
<div id="xxx"></div>
//js
var dragging = false
var position = null

xxx.addEventListener('mousedown',function(e){
  dragging = true
  position = [e.clientX, e.clientY]
})


document.addEventListener('mousemove', function(e){
  if(dragging === false){return}
  console.log('hi')
  const x = e.clientX
  const y = e.clientY
  const deltaX = x - position[0]
  const deltaY = y - position[1]
  const left = parseInt(xxx.style.left || 0)
  const top = parseInt(xxx.style.top || 0)
  xxx.style.left = left + deltaX + 'px'
  xxx.style.top = top + deltaY + 'px'
  position = [x, y]
})
document.addEventListener('mouseup', function(e){
  dragging = false
})

5. HTTP

1. 考:HTTP 狀態(tài)碼知道哪些?分別什么意思?

狀態(tài)碼 202 表示:服務(wù)器已接受請(qǐng)求,但尚未處理。
狀態(tài)碼 204 表示:請(qǐng)求處理成功,但沒(méi)有資源可返回。
狀態(tài)碼 206 表示:服務(wù)器已經(jīng)成功處理了部分 GET 請(qǐng)求。

狀態(tài)碼 301 表示:請(qǐng)求的資源已被永久的分配了新的 URI。
狀態(tài)碼 302 表示:請(qǐng)求的資源臨時(shí)的分配了新的 URI。

狀態(tài)碼 400 表示:請(qǐng)求報(bào)文中存在語(yǔ)法錯(cuò)誤。
狀態(tài)碼 401 表示:發(fā)送的請(qǐng)求需要有通過(guò) HTTP 認(rèn)證的認(rèn)證信息。
狀態(tài)碼 403 表示:對(duì)請(qǐng)求資源的訪問(wèn)被服務(wù)器拒絕了。
狀態(tài)碼 404 表示:服務(wù)器上無(wú)法找到請(qǐng)求的資源。

狀態(tài)碼 500 表示:服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤。
狀態(tài)碼 503 表示:服務(wù)器暫時(shí)處于超負(fù)債或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。

2. 大公司必考:HTTP 緩存有哪幾種?

  • 幾種緩存:
    Etag —— 是根據(jù)瀏覽器和服務(wù)器資源的唯一特征標(biāo)識(shí)符(比如MD5),果服務(wù)器發(fā)現(xiàn)ETag匹配不上,那么直接以常規(guī)GET 200回包形式將新的資源(當(dāng)然也包括了新的ETag)發(fā)給客戶(hù)端;如果ETag是一致的,則直接返回304知會(huì)客戶(hù)端直接使用本地緩存即可。
    Expires —— 自然也需要有個(gè)東西來(lái)啟用緩存和定義緩存時(shí)間,對(duì)http1.0而言,Expires就是做這件事的首部字段。 Expires的值對(duì)應(yīng)一個(gè)GMT(格林尼治時(shí)間),指定的是過(guò)期時(shí)間點(diǎn)(相對(duì)于服務(wù)器時(shí)間而言),來(lái)決定是否重新發(fā)起請(qǐng)求。這個(gè)方法會(huì)有bug,但是如果用戶(hù)的本地時(shí)間錯(cuò)亂了,可能會(huì)有問(wèn)題
    Cache-Control —— 設(shè)置一個(gè)時(shí)間段,它意味著該資源是從原服務(wù)器上取得的,且其緩存(新鮮度)的有效時(shí)間為一段時(shí)間,在后續(xù)這段時(shí)間內(nèi),用戶(hù)重新訪問(wèn)該資源則無(wú)須發(fā)送請(qǐng)求。 當(dāng)然這種組合的方式也會(huì)有些限制,比如 no-cache 就不能和 max-age、min-fresh、max-stale 一起搭配使用。
  • 之間區(qū)別:
    1. Expire和Cache-Control之間區(qū)別:前者是指定過(guò)期時(shí)間點(diǎn),而后者是指定有效緩存的時(shí)間段。
    2. Etag 和 Cache-Control之間的區(qū)別:Cache-Control的讀取時(shí)從瀏覽器本地文件中讀取的(無(wú)請(qǐng)求),而Etag是有請(qǐng)求的。
  • 近幾年有些大公司使用 PWA來(lái)緩存:那么瀏覽器的http緩存順序是什么?

3. 考:GET 和 POST 的區(qū)別

  • 錯(cuò)誤答案:

    1. POST安全,GET不安全,
    2. Get 有長(zhǎng)度限制(URL 長(zhǎng)度有1024字節(jié)),而Post沒(méi)有長(zhǎng)度限制(一般URL的長(zhǎng)度限制在4M),
    3. Get的參數(shù)是放在URL上,Post的參數(shù)放在消息體里面,
    4. Get只需要一個(gè)報(bào)文,Post需要兩個(gè)以上。
    5. Get是冪等的,Post不冪等。
  • 正確答案: Get是獲取數(shù)據(jù),Post是提交數(shù)據(jù)。其他的沒(méi)有本質(zhì)的區(qū)別。

4. ookie V.S. LocalStorage V.S. SessionStorage V.S. Session

  1. Cookie —— http協(xié)議的一部分
  2. session —— 存在服務(wù)器中的一小段哈希表(每個(gè)用戶(hù)都有自己的sessionId請(qǐng)求時(shí)存放在session中,),是服務(wù)器與瀏覽器之間的一段時(shí)間內(nèi)的會(huì)話。
  3. LocalStorage —— html5提供的API ,常用屬性有 setItem()/getItem()/LocalStorage.clear()/removeItem().實(shí)現(xiàn)了不同頁(yè)面中保存變量且其值不變。讓后端有了記憶力。
  • Cookie 與 session之間的關(guān)系:
    a. Cookie 存在瀏覽器的文件里,Session 存在服務(wù)器的文件里
    b.Session 是基于 Cookie 實(shí)現(xiàn)的,具體做法就是把 SessionID 存在 Cookie 里
  • Cookie 與 LocalStorage之間的關(guān)系:
    a. Cookie大小約4kb,LocalStorage一般有5M,
    b. Cookie 會(huì)被發(fā)送到服務(wù)器,LocalStorage不會(huì)
    c.Cookie 會(huì)存儲(chǔ)用戶(hù)信息,LocalStorage存儲(chǔ)沒(méi)用的東西。
  • SessionStorage 與 LocalStorage之間的關(guān)系:
    a.LocalStorage 一般不會(huì)自動(dòng)過(guò)期(除非用戶(hù)手動(dòng)清除),而 SessionStorage 在session(會(huì)話)結(jié)束時(shí)過(guò)期(如關(guān)閉瀏覽器).

5. HTTP1和 HTTP2的區(qū)別:

HTTP2的新特性:
a. 多路復(fù)用 (MultiPlexing) —— 解決了線頭阻塞的問(wèn)題,減少了 TCP 連接數(shù)量和 TCP 連接慢啟動(dòng)造成的問(wèn)題
b. 服務(wù)端推送 (Server Push) —— 瀏覽器發(fā)送一個(gè)請(qǐng)求,服務(wù)器主動(dòng)向?yàn)g覽器推送與這個(gè)請(qǐng)求相關(guān)的資源,這樣瀏覽器就不用發(fā)起后續(xù)請(qǐng)求。
c. Header 壓縮 (HPACK) —— 使用 HPACK 算法來(lái)壓縮首部?jī)?nèi)容
d. 二進(jìn)制分幀層 (Binary Framing Layer)—— 幀是數(shù)據(jù)傳輸?shù)淖钚挝?,以二進(jìn)制傳輸代替原本的明文傳輸,原本的報(bào)文消息被劃分為更小的數(shù)據(jù)幀。


6. 框架

6.1 Vue

1. 必考:watch 和 computed 和 methods 區(qū)別是什么?

watch是監(jiān)聽(tīng)數(shù)據(jù),computed是計(jì)算屬性;
watch和computed的最大區(qū)別是:computed是有緩存的,如果computed依賴(lài)的屬性沒(méi)有變化,computed屬性就不會(huì)重新激計(jì)算。而watch則是看到一次計(jì)算一次。

2. 必考:Vue 有哪些生命周期鉤子函數(shù)?分別有什么用?

Vue的生命周期:

  • beforeCreate : 實(shí)例初始化之后,數(shù)據(jù)觀測(cè)(data observoe)和事件配置(event/watcher)調(diào)用之前調(diào)用。
  • created : 實(shí)例初始化之后,數(shù)據(jù)觀測(cè)和事件配置調(diào)用之前調(diào)用。
  • beforeMount : 掛載組件開(kāi)始之前被調(diào)用,相關(guān)的render函數(shù)首次被調(diào)用
  • Mounted : el 被創(chuàng)建的vm.$el 替換,被掛載到實(shí)例上之后調(diào)用該鉤子,
  • beforeUpdate : 數(shù)據(jù)更新時(shí),發(fā)生在虛擬DOM 打補(bǔ)丁之前。
  • update : 由于數(shù)據(jù)更新導(dǎo)致的虛擬DOM重新渲染和打補(bǔ)丁之后會(huì)調(diào)用。
  • beforeDestroy :實(shí)例被銷(xiāo)毀之前,這個(gè)時(shí)候?qū)嵗匀煌耆捎谩?/li>
  • destroyed : 實(shí)例銷(xiāo)毀之后調(diào)用,調(diào)用后Vue 實(shí)例指示的所有東西都會(huì)被解除綁定,所有事件監(jiān)聽(tīng)都會(huì)被移除,所有 的子實(shí)例也會(huì)被銷(xiāo)毀。

Vue 的 Mounted生命周期進(jìn)行數(shù)據(jù)請(qǐng)求。

3. 考:Vue 如何實(shí)現(xiàn)組件間通信?
  1. 父子組件: $emit('xxx',data) on('xxx',function(){})
  2. 爺孫組件之間: 兩次 v-on 通過(guò)爺爺 => 爸爸 => 孫子
  3. 任意組件之間通信:eventBus = new Vue() 來(lái)通信;主要API: eventBus.on和eventBus.emit ; 任意組件之間也可以使用 Vuex來(lái)通信。
  • Vuex是專(zhuān)門(mén)為Vue應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理工具。Vuex的核心概念有以下幾種:
    state => 基本數(shù)據(jù)
    getters => 從基本數(shù)據(jù)派生的數(shù)據(jù)
    mutations => 提交更改數(shù)據(jù)的方法,同步!
    actions => 像一個(gè)裝飾器,包裹mutations,使之可以異步。
    modules => 模塊化Vuex
4. 必考:Vue 數(shù)據(jù)響應(yīng)式怎么做到的?(以前的概念:雙向綁定)

v-model實(shí)現(xiàn)響應(yīng)式:一句話總結(jié)就是:在數(shù)據(jù)渲染時(shí)使用prop渲染數(shù)據(jù),將prop綁定到子組件自身的數(shù)據(jù)上,修改數(shù)據(jù)時(shí)更新自身數(shù)據(jù)來(lái)替代prop,watch子組件自身數(shù)據(jù)的改變,觸發(fā)事件通知父組件更改綁定到prop的數(shù)據(jù)。

 a. Object.defineProperty 通過(guò) getter 和 setter 劫持了對(duì)象賦值的過(guò)程,在這個(gè)過(guò)程中可以進(jìn)行更新 dom 操作等等。
 b. Vue 不能檢測(cè)到對(duì)象屬性的添加或刪除,解決方法是手動(dòng)調(diào)用 Vue.set 或者 this.$set.
const component = {
    model : {
        prop : 'value',
        event : 'change'
    },
    props : ['value'],
    template:`
       <div> 
          <component v-model="value"></component>
      </div>
    `,
    methods : {
        handleInput(e){
          this.$emit('change',e.target.value)
        }
    }
}

5. 必考:Vue.set 是做什么用的?

Vue不能識(shí)別添加或刪除屬性的變化,vue不能在已經(jīng)創(chuàng)建的實(shí)力上動(dòng)態(tài)的添加新的根級(jí)響應(yīng)式屬性,而是必須用 Vue.set方法將響應(yīng)式屬性添加到嵌套對(duì)象上。

6. Vuex 你怎么用的?

Vuex是Vue.js應(yīng)用的程序的狀態(tài)管理工具。
其核心概念有以下:
state : 基本數(shù)據(jù)
getters : 基本數(shù)據(jù)派生的數(shù)據(jù)
mutations : 提交更改數(shù)據(jù)的方法,同步
actions : 相當(dāng)于裝飾器,封裝mutations,使之可以異步實(shí)現(xiàn)
mudules : 模塊化Vuex;

7. VueRouter 你怎么用的?

VueRouter是Vue.js官方的路由器管理。常考點(diǎn):重定向模式,history模式,導(dǎo)航守衛(wèi),路由懶加載(import('./Foo.vue')//組件路徑,返回promise對(duì)象)。
1. 重定向模式:“重定向”的意思是,當(dāng)用戶(hù)訪問(wèn) /a時(shí),URL 將會(huì)被替換成 /b,然后匹配路由為 /b。
2. 導(dǎo)航守衛(wèi):導(dǎo)航守衛(wèi)是路由跳轉(zhuǎn)過(guò)程中的一些鉤子函數(shù),路由跳轉(zhuǎn)是一個(gè)大的過(guò)程,這個(gè)大的過(guò)程分為跳轉(zhuǎn)前中后等多個(gè)細(xì)節(jié)過(guò)程,在每一個(gè)過(guò)程中都有一個(gè)函數(shù),這些個(gè)函數(shù)能讓你操作一些其他的操作的機(jī)會(huì),這就是導(dǎo)航守衛(wèi)。
導(dǎo)航守衛(wèi)有三種模式:
* 全局的:指路由在實(shí)例上直接操作的函數(shù):鉤子函數(shù)按執(zhí)行順序包括beforeEach、beforeResolve、afterEach三個(gè)。
* 單個(gè)路由獨(dú)享的:指單個(gè)路由配置的時(shí)候可以設(shè)置的函數(shù),只有一個(gè)函數(shù):beforeEnter,其參數(shù)有to,from,next.
* 組件內(nèi)的:組件內(nèi)執(zhí)行的鉤子函數(shù),類(lèi)似于組件內(nèi)的生命周期,鉤子函數(shù)按執(zhí)行順序包括beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave三個(gè)。
3. history模式:history模式采用html5的新特性,提供了兩個(gè)新方法,pushState ,replaceState,可以對(duì)瀏覽器歷史記錄棧進(jìn)行修改,以及popState事件的監(jiān)聽(tīng)到狀態(tài)變更。
4. 路由懶加載:當(dāng)打包構(gòu)建應(yīng)用時(shí),JavaScript 包會(huì)變得非常大,影響頁(yè)面加載。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問(wèn)的時(shí)候才加載對(duì)應(yīng)組件,這樣就更加高效了。
結(jié)合 Vue 的異步組件和 Webpack 的[代碼分割功能],輕松實(shí)現(xiàn)路由組件的懶加載。
使用方法:

import('./Foo.vue') // 返回 Promise。
const router = new VueRouter({
  routes: [
    {
      path: '/Foo',
      name: 'Home',
      component:() = import('../views/home')
        }
  ]
})
8. 路由守衛(wèi)是什么?(導(dǎo)航守衛(wèi)的鉤子函數(shù)來(lái)完成)
    • 業(yè)務(wù):
      • 監(jiān)聽(tīng)整個(gè)項(xiàng)目的路由變化情況 全局的前置守衛(wèi)
      • 監(jiān)聽(tīng)某個(gè)路由的變化情況 路由的獨(dú)享守衛(wèi)
      • 監(jiān)聽(tīng)的路由組件的路由變化情況 組件內(nèi)的導(dǎo)航守衛(wèi)

6.2 React

1. 必考:受控組件 V.S. 非受控組件

舉個(gè)例子:
<FInput value={x} onChange={fn} /> //受控組件
<FInput defaultValue={x} />//非受控組件

2. 必考:React 有哪些生命周期函數(shù)?分別有什么用?(Ajax 請(qǐng)求放在哪個(gè)階段?)
  • 生命周期函數(shù):
    一、初始化階段:
    getDefaultProps:獲取實(shí)例的默認(rèn)屬性
    getInitialState:獲取每個(gè)實(shí)例的初始化狀態(tài)
    constructor :聲明State變量
    componentWillMount:組件即將被裝載、渲染到頁(yè)面上
    render:組件在這里生成虛擬的DOM節(jié)點(diǎn)
    componentDidMount:組件真正在被裝載之后
    二、運(yùn)行中狀態(tài):
    componentWillReceiveProps:組件將要接收到屬性的時(shí)候調(diào)用
    shouldComponentUpdate:組件接受到新屬性或者新?tīng)顟B(tài)的時(shí)候(可以返回false,接收數(shù)據(jù)后不更新,阻止
    render調(diào)用,后面的函數(shù)不會(huì)被繼續(xù)執(zhí)行了)
    componentWillUpdate:組件即將更新不能修改屬性和狀態(tài)render:組件重新描繪
    componentDidUpdate:組件已經(jīng)更新三
    銷(xiāo)毀階段:
    componentWillUnmount:組件即將銷(xiāo)毀
  • 異步請(qǐng)求:ajax異步請(qǐng)求數(shù)據(jù)應(yīng)該放在 componentDidMount函數(shù)中。
3. 必考:React 如何實(shí)現(xiàn)組件間通信?
  • 父組件向子組件通訊: 父組件可以向子組件通過(guò)傳 props 的方式,向子組件進(jìn)行通訊
  • 子組件向父組件通訊: props+回調(diào)的方式,父組件向子組件傳遞props進(jìn)行通訊,此props為作用域?yàn)楦附M件自身的函數(shù),子組件調(diào)用該函數(shù),將子組件想要傳遞的信息,作為參數(shù),傳遞到父組件的作用域中
  • 兄弟組件通信: 找到這兩個(gè)兄弟節(jié)點(diǎn)共同的父節(jié)點(diǎn),結(jié)合上面兩種方式由父節(jié)點(diǎn)轉(zhuǎn)發(fā)信息進(jìn)行通信
  • 跨層級(jí)通信: Context設(shè)計(jì)目的是為了共享那些對(duì)于一個(gè)組件樹(shù)而言是“全局”的數(shù)據(jù),例如當(dāng)前認(rèn)證的用戶(hù)、主題或首選語(yǔ)言,對(duì)于跨越多層的全局?jǐn)?shù)據(jù)通過(guò)Context通信再適合不過(guò)
  • 發(fā)布訂閱模式: 發(fā)布者發(fā)布事件,訂閱者監(jiān)聽(tīng)事件并做出反應(yīng),我們可以通過(guò)引入event模塊進(jìn)行通信
  • 全局狀態(tài)管理工具: 借助Redux或者M(jìn)obx等全局狀態(tài)管理工具進(jìn)行通信,這種工具會(huì)維護(hù)一個(gè)全局狀態(tài)中心Store,并根據(jù)不同的事件產(chǎn)生新的狀態(tài)
4. 必考:shouldComponentUpdate 有什么用?

shouldComponentUpdate 這個(gè)方法用來(lái)判斷是否需要調(diào)用 render 方法重新描繪 dom。因?yàn)?dom 的描繪非常消耗性能,如果我們能在 shouldComponentUpdate 方法中能夠?qū)懗龈鼉?yōu)化的 dom diff 算法,可以極大的提高性能。

5. 考:虛擬 DOM 是什么?

react操作中render的結(jié)果并不能得到一個(gè)真正的DOM節(jié)點(diǎn),而是一個(gè)輕量級(jí)的javascript對(duì)象,我們稱(chēng)之為虛擬DOM。
就是用來(lái)模擬DOM的一個(gè)對(duì)象,有一些常用屬性,并且更新 UI 主要就是通過(guò)對(duì)比(DIFF)舊的虛擬 DOM 樹(shù) 和新的虛擬 DOM 樹(shù)的區(qū)別完成的,對(duì)真實(shí)的DOM進(jìn)行最小化的修改。

  • 優(yōu)點(diǎn):虛擬DOM具有批處理和高效的Diff算法,最終表現(xiàn)在DOM上的修改只是變更的部分,可以保證非常高效的渲染,優(yōu)化性能.
  • 缺點(diǎn):首次渲染大量DOM時(shí),由于多了一層虛擬DOM的計(jì)算,會(huì)比innerHTML插入慢
6. 必考:什么是高階組件?

高階組件是一個(gè)以組件為參數(shù)并返回一個(gè)新組件的函數(shù)。HOC 運(yùn)行你重用代碼、邏輯和引導(dǎo)抽象。最常見(jiàn)的高階組件是 Redux 的 connect 函數(shù)。比如 connect(mapState)(MyComponent) 接受組件 MyComponent,返回一個(gè)具有狀態(tài)的新 MyComponent 組件。除了簡(jiǎn)單分享工具庫(kù)和簡(jiǎn)單的組合,HOC 最好的方式是共享 React 組件之間的行為。如果你發(fā)現(xiàn)你在不同的地方寫(xiě)了大量代碼來(lái)做同一件事時(shí),就應(yīng)該考慮將代碼重構(gòu)為可重用的 HOC。

  • 高階組件是一個(gè)以組件為參數(shù)并返回一個(gè)新組件的函數(shù)。
  • 如果你發(fā)現(xiàn)你在不同的地方寫(xiě)了大量代碼來(lái)做同一件事時(shí),就應(yīng)該考慮將代碼重構(gòu)為可重用的 HOC。
7. React diff 的原理是什么?

對(duì)比兩顆DOM樹(shù)的區(qū)別,找出其中不同的部分。React diff 作為Virtual DOM的加速器,其算法上的改進(jìn)優(yōu)化是 React 整個(gè)界面渲染的基礎(chǔ),以及性能提高的保障,同時(shí)也是 React 源碼中最神秘、最不可思議的部分,本文將剖析 React diff 的不可思議之處。

8. 考 Redux 是什么?

Redux是一個(gè)庫(kù),它是javascript狀態(tài)容器(事件分發(fā)中心),提供可以預(yù)策劃的狀態(tài)管理。核心概念的名詞:action reducer store 單向數(shù)據(jù)流。
常用的API 有:store.dispatch(action) store.getState() 等。

9. connect 的原理是什么?

是react-redux庫(kù)提供的API,是基于react和redux封裝的函數(shù),負(fù)責(zé)連接React和Redux,把組件和store連接起來(lái)組成一個(gè)新的組件。
* 獲取state: connect通過(guò)context獲取Provider中的store,通過(guò)store.getState()獲取整個(gè)store tree 上所有state

  • 包裝原組件: 將state和action通過(guò)props的方式傳入到原組件內(nèi)部wrapWithConnect返回一個(gè)ReactComponent對(duì)象Connect,Connect重新render外部傳入的原組件WrappedComponent,并把connect中傳入的mapStateToProps, mapDispatchToProps與組件上原有的props合并后,通過(guò)屬性的方式傳給WrappedComponent
  • 監(jiān)聽(tīng)store tree變化: connect緩存了store tree中state的狀態(tài),通過(guò)當(dāng)前state狀態(tài)和變更前state狀態(tài)進(jìn)行比較,從而確定是否調(diào)用this.setState()方法觸發(fā)Connect及其子組件的重新渲染
10. (組件的)狀態(tài)(state)和屬性(props)之間有何不同?
  • State 是一種數(shù)據(jù)結(jié)構(gòu),用于組件掛載時(shí)所需數(shù)據(jù)的默認(rèn)值。State 可能會(huì)隨著時(shí)間的推移而發(fā)生突變,但多數(shù)時(shí)候是作為用戶(hù)事件行為的結(jié)果。
  • Props(properties 的簡(jiǎn)寫(xiě))則是組件的配置。props 由父組件傳遞給子組件,并且就子組件而言,props 是不可變的(immutable)。組件不能改變自身的 props,但是可以把其子組件的 props 放在一起(統(tǒng)一管理)。Props 也不僅僅是數(shù)據(jù)--回調(diào)函數(shù)也可以通過(guò) props 傳遞。

7. TypeScript

1. never 類(lèi)型是什么?

never是不應(yīng)該出現(xiàn)的類(lèi)型,never類(lèi)型可以理解為沒(méi)有返回指的函數(shù)或者總是會(huì)拋出錯(cuò)誤的函數(shù)。

//舉例:
function(){while return{}}//如果函數(shù)內(nèi)含有 while(true) {}
function foo(){
      throw new Error('not implemented')
  }
//foo的返回類(lèi)型是never

【注意】never僅能被賦值給另一個(gè)never
viod表示沒(méi)有任何類(lèi)型。never表示永遠(yuǎn)不存在的類(lèi)型。

2. TypeScript 比起 JavaScript 有什么優(yōu)點(diǎn)?

目前主流框架都已經(jīng)使用了typescript,Vue3.0\React都已經(jīng)使用了,typescript是javascript的超集,也就是說(shuō)js能做到的東西typescript都能做到,javascript程序員會(huì)的東西typescript程序員都會(huì),js程序員不會(huì)的他們也都會(huì),所以typescript一定是以后的主流。其優(yōu)點(diǎn):typescript提供了類(lèi)型約束,因此可控,更容易重構(gòu),做更大的項(xiàng)目,更容易維護(hù)。

  • bug 顯著減少,之前會(huì)遇到的 xxx 為空的問(wèn)題幾乎不會(huì)出現(xiàn)了,類(lèi)型相關(guān) bug 直線減少。
  • 應(yīng)用更可控,當(dāng)你需要約束某些代碼的時(shí)候,用類(lèi)型就能很簡(jiǎn)單地做到,比如 React 里強(qiáng)制寫(xiě) displayName 方便調(diào)試。
  • 查文檔更方便,以前要打開(kāi)瀏覽器看文檔,現(xiàn)在直接查看定義就基本明白了。

項(xiàng)目目前只支持 JS,也沒(méi)有關(guān)系,只需要加一個(gè) ts-loader 或者 awesome-typescript-loader 就能提供 TypeScript 支持,TS 可以和 JS 共存。學(xué)完 JS 后,只需要學(xué)習(xí)一下類(lèi)型聲明就可以掌握 TS 了。TS 就是在 JS 上加上類(lèi)型聲明,這樣我們就能知道代碼是否「大概」正確。
另外,這種方式速度非??欤斓侥阒灰薷拇a,TS 就能告訴你代碼是否「大概」正確。
從而避免很多 bug。


8. Webpack

參考鏈接【https://zhuanlan.zhihu.com/p/44438844

1. 必考:有哪些常見(jiàn) loader 和 plugin,你用過(guò)哪些?

三者都是前端構(gòu)建工具,grunt和gulp在早期比較流行,現(xiàn)在webpack相對(duì)來(lái)說(shuō)比較主流,webpack會(huì)自動(dòng)地遞歸解析入口所需要加載的所有資源文件,然后用不同的Loader來(lái)處理不同的文件,用Plugin來(lái)擴(kuò)展webpack功能。
常見(jiàn)的loader—— 加載器

    * html —— pug-loader    markdown-loader
    * css —— style-loader   less-loader   scss-loader  postloader
    * js ——  babel-loader   
    * 圖片 —— url-loader  eslint-loader
  • file-loader:把文件輸出到一個(gè)文件夾中,在代碼中通過(guò)相對(duì) URL 去引用輸出的文件
  • url-loader:和 file-loader 類(lèi)似,但是能在文件很小的情況下以 base64 的方式把文件內(nèi)容注入到代碼中去
  • source-map-loader:加載額外的 Source Map 文件,以方便斷點(diǎn)調(diào)試
  • image-loader:加載并且壓縮圖片文件
  • babel-loader:把 ES6 轉(zhuǎn)換成 ES5
  • css-loader:加載 CSS,支持模塊化、壓縮、文件導(dǎo)入等特性
  • style-loader:把 CSS 代碼注入到 JavaScript 中,通過(guò) DOM 操作去加載 CSS。
  • eslint-loader:通過(guò) ESLint 檢查 JavaScript 代碼

常見(jiàn)的plugin—— 用Plugin來(lái)擴(kuò)展webpack功能

  • html —— html-webpack-plugin
  • css —— ex tract-text-plugin
  • js ——
    • define-plugin:定義環(huán)境變量
    • commons-chunk-plugin:提取公共代碼
    • uglifyjs-webpack-plugin:通過(guò)UglifyES壓縮ES6代碼

2. loader 和 plugin的區(qū)別是什么?

  1. 不同的作用:
    a. Loader直譯為"加載器"。Webpack將一切文件視為模塊,但是webpack原生是只能解析js文件,如果想將其他文件也打包的話,就會(huì)用到loader。 所以Loader的作用是讓webpack擁有了加載和解析非JavaScript文件的能力。
    b . Plugin直譯為"插件"。Plugin可以擴(kuò)展webpack的功能,讓webpack具有更多的靈活性。 在 Webpack 運(yùn)行的生命周期中會(huì)廣播出許多事件,Plugin 可以監(jiān)聽(tīng)這些事件,在合適的時(shí)機(jī)通過(guò) Webpack 提供的 API 改變輸出結(jié)果。
  2. 不同的用法:
    a. Loader在module.rules中配置,也就是說(shuō)他作為模塊的解析規(guī)則而存在。 類(lèi)型為數(shù)組,每一項(xiàng)都是一個(gè)Object,里面描述了對(duì)于什么類(lèi)型的文件(test),使用什么加載(loader)和使用的參數(shù)(options)
    b. Plugin在plugins中單獨(dú)配置。 類(lèi)型為數(shù)組,每一項(xiàng)是一個(gè)plugin的實(shí)例,參數(shù)都通過(guò)構(gòu)造函數(shù)傳入。

3. 必考:如何按需加載代碼?

Vue UI組件庫(kù)的按需加載 為了快速開(kāi)發(fā)前端項(xiàng)目,經(jīng)常會(huì)引入現(xiàn)成的UI組件庫(kù)如ElementUI、iView等,但是他們的體積和他們所提供的功能一樣,是很龐大的。 而通常情況下,我們僅僅需要少量的幾個(gè)組件就足夠了,但是我們卻將龐大的組件庫(kù)打包到我們的源碼中,造成了不必要的開(kāi)銷(xiāo)。

不過(guò)很多組件庫(kù)已經(jīng)提供了現(xiàn)成的解決方案,如Element出品的[babel-plugin-component](https://link.zhihu.com/?target=https%3A//github.com/ElementUI/babel-plugin-component)和AntDesign出品的[babel-plugin-import](https://link.zhihu.com/?target=https%3A//github.com/ant-design/babel-plugin-import) 安裝以上插件后,在.babelrc配置中或babel-loader的參數(shù)中進(jìn)行設(shè)置,即可實(shí)現(xiàn)組件按需加載了。

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

單頁(yè)應(yīng)用的按需加載 現(xiàn)在很多前端項(xiàng)目都是通過(guò)單頁(yè)應(yīng)用的方式開(kāi)發(fā)的,但是隨著業(yè)務(wù)的不斷擴(kuò)展,會(huì)面臨一個(gè)嚴(yán)峻的問(wèn)題——首次加載的代碼量會(huì)越來(lái)越多,影響用戶(hù)的體驗(yàn)。

通過(guò)import(*)語(yǔ)句來(lái)控制加載時(shí)機(jī),webpack內(nèi)置了對(duì)于import(*)的解析,會(huì)將import(*)中引入的模塊作為一個(gè)新的入口在生成一個(gè)chunk。 當(dāng)代碼執(zhí)行到import(*)語(yǔ)句時(shí),會(huì)去加載Chunk對(duì)應(yīng)生成的文件。import()會(huì)返回一個(gè)Promise對(duì)象,所以為了讓瀏覽器支持,需要事先注入Promise polyfill

4. 必考:如何提高構(gòu)建速度?

  • 多入口情況下,使用CommonsChunkPlugin來(lái)提取公共代碼
  • 通過(guò)externals配置來(lái)提取常用庫(kù)
  • 利用DllPlugin和DllReferencePlugin預(yù)編譯資源模塊 通過(guò)DllPlugin來(lái)對(duì)那些我們引用但是絕對(duì)不會(huì)修改的npm包來(lái)進(jìn)行
  • 預(yù)編譯,再通過(guò)DllReferencePlugin將預(yù)編譯的模塊加載進(jìn)來(lái)。
  • 使用Happypack 實(shí)現(xiàn)多線程加速編譯(多核CPU進(jìn)行打包)
  • 使用webpack-uglify-parallel來(lái)提升uglifyPlugin的壓縮速度。 原理上webpack-uglify-parallel采用了多核并行壓縮來(lái)提升壓縮速度
  • 使用Tree-shaking和Scope Hoisting來(lái)剔除多余代碼(Tree-Shaking不要說(shuō),說(shuō)了會(huì)追問(wèn))

5. 義出的文件過(guò)大怎么辦?

  • commonsChunkPlugin: 提取通用模塊(最新的webpack已經(jīng)不使用了這個(gè)插件)
  • 壓縮 js,css,圖片
  • 使用動(dòng)態(tài)加載:import()

9. 網(wǎng)絡(luò)安全

1. 考:什么是 XSS?如何預(yù)防?

XSS —— Cross-Site Scripting 跨站腳本。

用戶(hù) A 提交評(píng)論「小谷你好」到服務(wù)器,然后用戶(hù) B 來(lái)訪問(wèn)網(wǎng)站,看到了 A 的評(píng)論「小谷你好」,這里沒(méi)有 XSS。

惡意用戶(hù) H 提交評(píng)論「<script>console.log(document.cookie)</script>」,然后用戶(hù) B 來(lái)訪問(wèn)網(wǎng)站,這段腳本在 B 的瀏覽器直接執(zhí)行,惡意用戶(hù) H 的腳本就可以任意操作 B 的 cookie,而 B 對(duì)此毫無(wú)察覺(jué)。有了 cookie,惡意用戶(hù) H 就可以偽造 B 的登錄信息,隨意訪問(wèn) B 的隱私了。而 B 始終被蒙在鼓里。
防范方法:

    1. 后臺(tái)模板問(wèn)題
<p>
評(píng)論內(nèi)容:<?php echo $content; ?>
</p>

$content 的內(nèi)容,沒(méi)有經(jīng)過(guò)任何過(guò)濾,原樣輸出。

要解決這個(gè)原因,只需要后臺(tái)輸出的時(shí)候,將可疑的符號(hào) < 符號(hào)變成 < (HTML實(shí)體)就行。

  • 前端代碼問(wèn)題
$p.html(content)
或者

$p = $('<p>'+ content +'</p>')

content 內(nèi)容又被原樣輸出了。解決辦法就是不要自己拼 HTML,盡量使用 text 方法。如果一定要使用 HTML,就把可疑符號(hào)變成 HTML 實(shí)體。

2. 必考:什么是 CSRF?如何預(yù)防?

Cross Site Request Forgery,跨站請(qǐng)求偽造。其原理是攻擊者構(gòu)造網(wǎng)站后臺(tái)某個(gè)功能接口的請(qǐng)求地址,誘導(dǎo)用戶(hù)去點(diǎn)擊或者用特殊方法讓該請(qǐng)求地址自動(dòng)加載。用戶(hù)在登錄狀態(tài)下這個(gè)請(qǐng)求被服務(wù)端接收后會(huì)被誤以為是用戶(hù)合法的操作。對(duì)于 GET 形式的接口地址可輕易被攻擊,對(duì)于 POST 形式的接口地址也不是百分百安全,攻擊者可誘導(dǎo)用戶(hù)進(jìn)入帶 Form 表單可用POST方式提交參數(shù)的頁(yè)面。


10. 開(kāi)放題目

1. 必考:你遇到最難的問(wèn)題是怎樣的?

2. 你在團(tuán)隊(duì)的突出貢獻(xiàn)是什么?

3. 最近在關(guān)注什么新技術(shù)

阮一峰的課本,(微博)。

4. 有沒(méi)有看什么源碼,看了后有什么記憶深刻的地方,有什么收獲

看一些源代碼,命名規(guī)范的重要性,從而提高代碼的可讀性。
設(shè)計(jì)模式,輪播思路。


11. 個(gè)性化題目

1. PWA

2. echarts.js / d3.js

3. three.js

4. flutter

5. SSR


12. 算法 + 數(shù)據(jù)庫(kù)


13. git 版本控制:

1. github創(chuàng)建倉(cāng)庫(kù):

 命令行:
git init
git commit -m "first commit"
git add --all
創(chuàng)建遠(yuǎn)程倉(cāng)庫(kù)(復(fù)制即可)
git push -u origin master

2. 查看版本:

git log (--prety=online //輸出信息太多時(shí)可以用) //顯示最近到最遠(yuǎn)的提交日志
git checkout -b <new-branch-name>  //如果版本次數(shù)太多可以寫(xiě)成  HEAD~100
//和上一步git checkout 一樣功能的命令行有 :
gitk  //功能和git checkout -b <new-branch-name>一樣,一圖形化工具顯示顯示已提交版本
  • 注意:
    git log 很很多選項(xiàng):
選項(xiàng)         說(shuō)明
-p            按補(bǔ)丁格式顯示每個(gè)更新之間的差異
--word-diff       按 word diff 格式顯示差異
--stat          顯示每次更新的文件修改統(tǒng)計(jì)信息
--shortstat       只顯示 --stat 中最后的行數(shù)修改添加移除統(tǒng)計(jì)
--name-only       僅在提交信息后顯示已修改的文件清單
--name-status     顯示新增、修改、刪除的文件清單
--abbrev-commit   僅顯示 SHA-1 的前幾個(gè)字符,而非所有的 40 個(gè)字符
--relative-date   使用較短的相對(duì)時(shí)間顯示(比如,“2 weeks ago”)
--graph        顯示 ASCII 圖形表示的分支合并歷史
--pretty        使用其他格式顯示歷史提交信息可用的選項(xiàng)包括oneline,short,full,fuller 和format(后跟指定格式)
--oneline        `--pretty=oneline --abbrev-commit` 的簡(jiǎn)化用法

3. 切換版本:

git reset --HEAD^ //返回第一個(gè)版本
git reflog 命令行提供了查詢(xún)commit id 的功能,查詢(xún)到的id 寫(xiě)在git reset后面就會(huì)返回指定版本。


14. 垃圾回收機(jī)質(zhì):

javascript有自動(dòng)垃圾收集機(jī)制(GC:Garbage Collection),也就是說(shuō)執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過(guò)程中使用的內(nèi)存,開(kāi)發(fā)人員不用再過(guò)于擔(dān)心內(nèi)存使用的問(wèn)題,所需內(nèi)存的分配與無(wú)用內(nèi)存的回收完全實(shí)現(xiàn)了自動(dòng)管理。
JS的垃圾回收機(jī)制是為了以防內(nèi)存泄漏,內(nèi)存泄漏的含義就是當(dāng)已經(jīng)不需要某塊內(nèi)存時(shí)這塊內(nèi)存還存在著,垃圾回收機(jī)制就是間歇的不定期的尋找到不再使用的變量,并釋放掉它們所指向的內(nèi)存。

  • 垃圾回收方式:
    1. 標(biāo)記清除:大部分瀏覽器以此方式進(jìn)行垃圾回收,當(dāng)變量進(jìn)入執(zhí)行環(huán)境(函數(shù)中聲明變量)的時(shí)候,垃圾回收器將其標(biāo)記為“進(jìn)入環(huán)境”,當(dāng)變量離開(kāi)環(huán)境的時(shí)候(函數(shù)執(zhí)行結(jié)束)將其標(biāo)記為“離開(kāi)環(huán)境”,在離開(kāi)環(huán)境之后還有的變量則是需要被刪除的變量。標(biāo)記方式不定,可以是某個(gè)特殊位的反轉(zhuǎn)或維護(hù)一個(gè)列表等。
      垃圾收集器給內(nèi)存中的所有變量都加上標(biāo)記,然后去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。在此之后再被加上的標(biāo)記的變量即為需要回收的變量,因?yàn)榄h(huán)境中的變量已經(jīng)無(wú)法訪問(wèn)到這些變量。
    2. 引用計(jì)數(shù):這種方式常常會(huì)引起內(nèi)存泄漏,低版本的IE使用這種方式。機(jī)制就是跟蹤一個(gè)值的引用次數(shù),當(dāng)聲明一個(gè)變量并將一個(gè)引用類(lèi)型賦值給該變量時(shí)該值引用次數(shù)加1,當(dāng)這個(gè)變量指向其他一個(gè)時(shí)該值的引用次數(shù)便減一。當(dāng)該值引用次數(shù)為0時(shí)就會(huì)被回收。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.清除盒模型的浮動(dòng)方式 way 1: 最后追加元素, 設(shè)置 clear:both; way 2: 偽元素清除 d...
    Hyb_7818閱讀 842評(píng)論 0 0
  • 1. flex實(shí)現(xiàn)骰子5點(diǎn)布局 簡(jiǎn)單的思路:1.flex布局橫向排列,flex-flow:wrap 可以折行2.通...
    Jsonzhang閱讀 734評(píng)論 0 2
  • 【轉(zhuǎn)載】CSDN - 張林blog http://blog.csdn.net/XIAOZHUXMEN/articl...
    竿牘閱讀 3,588評(píng)論 1 14
  • 阿里 使用過(guò)的koa2中間件 koa-body原理 介紹自己寫(xiě)過(guò)的中間件 有沒(méi)有涉及到Cluster 介紹pm2 ...
    隨便娶個(gè)名字閱讀 1,266評(píng)論 0 1
  • 我還沒(méi)到中年,也還沒(méi)活成自己討厭的樣子,并且一直在追求自己喜歡的事情。 一、對(duì)自己的理想保持熱忱 我覺(jué)得能夠讓自己...
    錦夏ta閱讀 416評(píng)論 0 0

友情鏈接更多精彩內(nèi)容