快速構建初級前端知識體系,面試題彙總

語言: CN / TW / HK
3.org/1999/xhtml">

theme: juejin highlight: vs2015


前言

原文來自 我的個人部落格

最近一直在學習 vue3 原始碼,搞的頭有點大,用這篇文章來換換腦子~

PS:面試題下附有解答,解答會結合我的思考以及擴充套件,僅供參考,如果有誤麻煩指出,好了就醬~

1. HTML

在面試中,HTML 的面試題相對來說比較簡單,一般面試官不會花太多時間去關注 HTML 的細節。

1.1 如何理解 HTML 語義化

語義化的含義就是用正確的標籤做正確的事情html 語義化就是讓頁面的內容結構化。

打個比方就是,如果我要實現一個一級標題,可以用 div+css 設定樣式字型來達到效果,也可以用 h1,前者當然也能實現效果,但是語義化的方式還是使用 h1 標籤,因為我們一看到 h1 標籤就會知道他是一個 一級標題,這就是 html 語義化。

  1. 語義化不僅能方便我們開發人員閱讀維護理解
  2. 還有利於搜尋引擎的建立索引、抓取(SEO 優化
  3. 而且有還利於不同裝置的解析(螢幕閱讀器,盲人閱讀器等)

1.2 談談 SEO

這個問題可以從三個方向回答: 1. SEO 是什麼? 2. 它的原理是? 3. SEO 優化方法有哪些?

回答: 1. SEO(Search Engine Optimization),意思就是搜尋引擎優化。通俗點講就是提高你的網頁在搜尋結果中的排名(排名越高越靠前) 2. SEO 的原理可以大致分為四個步驟: 1. 爬行和抓取:搜尋引擎派出稱為爬蟲的程式,從一隻網頁出發,就像正常使用者的瀏覽器一樣訪問這些網頁抓取檔案,並且會跟蹤網頁上的連結,訪問更多的網頁。 2. 索引:搜尋引擎將爬取的網頁檔案分解、分析存入資料庫,這個過程就是索引。 3. 搜尋詞處理:使用者輸入關鍵詞,點選搜尋後,搜尋引擎會進行 分詞 檢查拼音 錯別字 等等。。。 4. 排序:搜尋引擎從索引資料庫中找出所有包含搜尋詞的網頁,並按照計演算法進行排序返回。 4. SEO 優化可以分為內部優化和外部優化 - 內部優化 - 標籤優化:語義化標籤、META 標籤 - 網站內容更新:每天保持站內的更新(主要是文章的更新等) - 伺服器端渲染SSR) - 內部連結的優化,包括相關性連結(Tag 標籤),錨文字連結,各導航連結,及圖片連結 - 外部優化: - 外部連結類別:部落格、論壇、B2B、新聞、分類資訊、貼吧、知道、百科、相關資訊網等儘量保持連結的多樣性 - 外鏈運營:每天新增一定數量的外部連結,使關鍵詞排名穩定提升。 - 外鏈選擇:與一些和你網站相關性比較高,整體質量比較好的網站交換友情連結,鞏固穩定關鍵詞排名

1.3 HTML 有哪些塊級元素,有哪些行內元素以及他們的區別

display: block/inline/inline-block - 常用的塊級元素:div、p、h1-6、table、form、ul、dl - 常用的行內元素:a、span、br、label、strong - 常用的內聯塊狀元素有:img、input

  1. 預設情況下塊級元素會獨佔一行而行內元素不會
  2. 塊級元素可以設定 width, height 屬性而行內元素設定無效
  3. 塊級元素可以設定 marginpadding,而行內元素只有水平方向有效,豎直方向無效
  4. 塊級元素可以包含行內元素和塊級元素。行內元素不能包含塊級元素

1.4 什麼是 HTML5,和 HTML 的區別是?

HTML5HTML 的新標準,其主要目標是無需任何額外的外掛如 FlashSilverlight 等,就可以傳輸所有內容。它囊括了動畫、影片、豐富的圖形使用者介面等。

區別:

  • 從文件宣告型別上看:HTML 是很長的一段程式碼,很難記住。HTML5 卻只有簡簡單單的宣告,方便記憶。

```html

`` - 從語義結構上看:HTML4.0**沒有體現結構語義化的標籤**,通常都是這樣來命名的

,這樣表示網站的頭部。HTML5**在語義上卻有很大的優勢**。提供了一些新的標籤,比如:
`。

1.5 HTML5 有哪些新特性?

  1. 新增語義化標籤navheaderfooterasidesectionarticle
  2. 音訊、影片標籤audiovideo
  3. 資料儲存localStoragesessionStorage
  4. canvas(畫布)、Geolocation(地理定位)、websocket(通訊協議)
  5. input 標籤新增屬性placeholderautocompleteautofocusrequired
  6. history APIgoforwardbackpushstate

2. CSS

不多說了,前端面試 CSS 是必考知識,不過關直接回家

2.1 談談你對盒模型的理解

思路: 1. 先講盒模型是什麼 2. 再介紹兩種盒模型的區別 3. 可以再提一下 box-sizing 這個屬性

回答: 1. 當對一個文件進行佈局layout)的時候,瀏覽器的渲染引擎會根據標準之一的 CSS基礎框盒模型(CSS basic box model),將所有元素表示為一個個矩形的盒子(box)。通俗來講就是網頁是由一個一個盒子組成的,而這個盒子是有自己的標準的,一個盒子由以下四個部分組成:

- `content(盒子的內容)`
- `padding(盒子的內邊距)`
- `border(盒子的邊框)`
- `margin(盒子的外邊距)`
  1. CSS 中,盒子模型可以分成:標準盒子模型怪異盒子模型

    • 標準盒子模型:瀏覽器預設的盒子模型

      盒子總寬度 = width + padding + border + margin 盒子總高度 = height + padding + border + margin

      width/height 只是內容高度,不包含 paddingborder

    • 怪異盒子模型

      盒子總寬度 = width + margin 盒子總高度 = height + margin

      width/height 包含了 paddingborder

  2. CSS 中的 box-sizing 屬性定義了引擎應該如何計算一個元素的總寬度和總高度

  3. content-box 預設值,元素的 width/height 不包含paddingborder,與 標準盒子模型表現一致

  4. border-box 元素的 width/height 包含 paddingborder與怪異盒子模型表現一致
  5. inherit 指定 box-sizing 屬性的值,應該從父元素繼承

image.png

2.2 margin 的縱向重疊問題

下面我們來看一段程式碼: ```html

aaa

bbb

ccc

ddd

eee

`` 請問: 1.aaa距離最頂部多少 px? 2.bbb距離ccc多少 px? 3.ddd距離eee` 多少 px?

公佈答案: 分別是 20px 50px 50px

image.png

常見的重疊現象 1. 同一層相鄰元素之間,相鄰元素外邊距重疊 2. 父元素與子元素重疊 3. 空的塊級元素重疊

解決方法: 1. 相鄰元素之間: 1. 底部元素設定為浮動 float:left; 2. 底部元素的 position 的值為 absolute/fixed 3. 在設定 margin-top/bottom 值時統一設定上或下(推薦) 2. 父子元素之間: 1. 外層元素新增 padding 2. 外層元素 overflow:hidden/auto(推薦); 3. 外層元素透明邊框 border:1px solid transparent; 4. 內層元素絕對定位 postion:absolute: 5. 內層元素加 float:left;或 display:inline-block;

2.3 margin-left/right/bottom/top 分別設為負值會怎麼樣?

  • margin-topmargin-left 負值,元素向上、向左移動
  • margin-right 負值,右側元素左移,自身不受影響
  • margin-bottom 負值,下方元素上移,自身不受影響

第一點應該很好理解,下面這張圖分別展示了第二和第三點。 image.png

2.4 BFC 的理解

1. 定義:

BFC 全稱:Block Formatting Context, 名為 "塊級格式化上下文"。(全程說出來也能加分)

W3C 官方解釋為:BFC 它決定了元素如何對其內容進行定位,以及與其它元素的關係和相互作用,當涉及到視覺化佈局時,Block Formatting Context 提供了一個環境,HTML 在這個環境中按照一定的規則進行佈局。

簡單來說就是,BFC 就是一塊獨立渲染區域,內部元素的渲染不會影響邊界以外的元素。BFC 有自己的規則和觸發條件。

2. 規則 - BFC 就是一個塊級元素,塊級元素會在垂直方向一個接一個的排列 - BFC 就是頁面中的一個隔離的獨立容器,容器裡的標籤不會影響到外部標籤 - 垂直方向的距離由 margin 決定, 屬於同一個 BFC 的兩個相鄰的標籤外邊距會發生重疊 - 計算 BFC 的高度時,浮動元素也參與計算

3. 怎樣觸發 BFC

  1. overflow: hidden
  2. display: flex / inline-block / table-cell
  3. position: absolute / fixed

4. BFC 解決了什麼問題

1. 使用 float 脫離文件流,造成高度塌陷

```html

```

效果:

image.png

container 觸發 BFC 就可解決問題,例如 給 container 元素 新增 display:inline-block。效果:

image.png

2. margin 邊距重疊問題

這個問題 2.2 已經講過: ```

```

效果以及觸發 BFC 後的效果如下:

image.png

2.5 css 如何畫三角形

原理:css 畫三角形的原理就是通過 border 畫的,因為在 cssborder 並不是一個矩形,而是一個梯形,在 box 的內容越來越小時,border 的一條底邊也會越來越小,直到 box 的寬高都是 0 時,此時的四條 border 就組成了四個三角形,將其他三個隱藏,就能得到一個三角形。

image.png

```css

```

效果:

image.png

2.6 absolute 和 relative 分別依據什麼定位?

  • relative 依據自身定位
  • absolute 依據最近一層的定位元素

2.8 居中對齊的實現方式

居中的方式有很多,我最常用的還是 flexboxcss display:flex; justify-content: center; // 水平居中 align-items: center; // 垂直居中

另外常見的還有: 1. text-align: center 2. line-height 和 height 設定成一樣的高度 3. margin: auto 0 4. 絕對定位 + margin 負值 5. 絕對定位 + transform 負值

2.9 line-height 的繼承問題

請問下面程式碼 p 標籤的行高是多少 ```html

AAA

```

因為上面的程式碼中 p 標籤沒有自己的行高,所以會繼承 body 的行高,這題考的就是行高是如何繼承的。

規則: 1. 寫具體數值,如 30px, 則直接繼承該值 2. 寫比例,如 2 / 1.5,則繼承該比例 3. 寫百分比,如 200%, 則繼承計算出來的值

答案:40px

2.10 rem 是什麼?

rem 是一個長度單位: - px,絕對長度單位,最常用 - em,相對長度單位,相對於父元素,不常用 - rem,相對長度單位,相對於 html 根元素,常用於響應式佈局

2.11 CSS3 新增了哪些特性?

1. css3 中新增了一些選擇器,主要為下圖:

image.png

2. css3 中新增了一些樣式 - 邊框:border-radius box-shadow border-image

  • 背景:background-clipbackground-originbackground-sizebackground-break

  • 文字:word-wrap text-overflow text-shadow text-decoration

  • 顏色:rgbahsla

  • 動畫相關: transition transform animation

  • 漸變:linear-gradient radial-gradient

  • 其他: flex佈局 grid佈局

(上面提及的屬性都應該去了解!)

3. JS 基礎-變數型別和計算

不會變數,別說會 JS

3.1 JavaScript 中的資料型別有哪些?

JS 中的資料型別分為 值型別(基本型別)引用型別(物件型別)

  • 基本型別有:undefinednullbooleanstringnumbersymbolBigInt 七種
  • 引用型別有:ObjectFunctionArrayRegExpDate 等等

3.2 null 和 undefined 有什麼區別?

首先 undefinednull 都是基本資料型別

undefined 翻譯過來是 未定義。表示 此處應該有一個值,但是還沒有定義

null 翻譯過來是 空的。表示 沒有物件,即此處不應該有值

典型用法: 1. 一般變數聲明瞭但還沒有定義的時候會返回 undefined,函式中沒有返回值時預設返回undefined,以及函式引數沒提供時這個引數也是 undefined

  1. null 作為物件原型鏈的終點。null 作為函式的引數,表示該函式的引數不是物件。

typeof undefined 值為 undefinedtypeof null'object'

js undefined == null // true undefined === null // false

至於為什麼在 js 中會有 nullundefined 嗎,就要從歷史談起了,建議閱讀一下阮一峰老師的文章 undefined與null的區別

我的總結就是,JS 最初是隻設計了一種表示 “無” 的值的就是 null,這點很好理解就像 java 以及其他語言中的 null 一樣,但是在 JS 中的資料型別分為 基本型別物件型別JS 的作者認為表示 “無” 的變數最好不是一個物件,另外 JS 早期沒有錯誤處理機制,有時null 自動轉為 0,很不容易發現錯誤,而 undefined 會轉換成 NaN

3.3 談談 Javascript 中的型別轉換機制

JS 的型別轉換分為:顯式轉換隱式轉換

1. 顯式轉換

顯式轉換常見的方法有:

  • Number將任意型別的值轉化為數值

```js Number(324) // 324

// 字串:如果可以被解析為數值,則轉換為相應的數值 Number('324') // 324

// 字串:如果不可以被解析為數值,返回 NaN Number('324abc') // NaN

// 空字串轉為0 Number('') // 0

// 布林值:true 轉成 1,false 轉成 0 Number(true) // 1 Number(false) // 0

// undefined:轉成 NaN Number(undefined) // NaN

// null:轉成0 Number(null) // 0

// 物件:通常轉換成NaN(除了只包含單個數值的陣列) Number({a: 1}) // NaN Number([1, 2, 3]) // NaN Number([5]) // 5 ```

  • parseInt:和 Number 相比,Number 會更嚴格一些,只要有一個字元無法轉換成數值,整個字串就會被轉為 NaN,而 parseInt 函式逐個解析字元,遇到不能轉換的字元就停下來例如:

js Number('32a3') // NaN parseInt('32a3') // 32

  • String可以將任意型別的值轉化成字串

```js // 數值:轉為相應的字串 String(1) // "1"

//字串:轉換後還是原來的值 String("a") // "a"

//布林值:true轉為字串"true",false轉為字串"false" String(true) // "true"

//undefined:轉為字串"undefined" String(undefined) // "undefined"

//null:轉為字串"null" String(null) // "null"

//物件 String({a: 1}) // "[object Object]" String([1, 2, 3]) // "1,2,3" ```

  • Boolean可以將任意型別的值轉為布林值

js Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean(NaN) // false Boolean('') // false Boolean({}) // true Boolean([]) // true Boolean(new Boolean(false)) // true

  1. 隱式轉換

在 JS 中,很多時候會發生隱式轉換,我們可以歸納為兩種場景: 1. 比較運算(==!=><)、ifwhile 需要布林值的地方 2. 算術運算(+-*/%

上面的場景有個前提就是運算子兩邊的運算元要是不同一型別的

  • 自動轉換為布林值

在需要布林值的地方,就會將非布林值的引數自動轉為布林值,系統內部會呼叫Boolean函式。

undefined null false +0 -0 NaN "" 這些都會被轉化成 false,其他都換被轉化成 true

  • 自動轉換成字串

遇到預期為字串的地方,就會將非字串的值自動轉為字串

常發生在 + 運算中,一旦存在字串,則會進行字串拼接操作 js '5' + 1 // '51' '5' + true // "5true" '5' + false // "5false" '5' + {} // "5[object Object]" '5' + [] // "5" '5' + function (){} // "5function (){}" '5' + undefined // "5undefined" '5' + null // "5null"

  • 自動轉換成數值

除了 + 有可能把運運算元轉為字串,其他運算子都會把運運算元自動轉成數值

js '5' - '2' // 3 '5' * '2' // 10 true - 1 // 0 false - 1 // -1 '1' - 1 // 0 '5' * [] // 0 false / '5' // 0 'abc' - 1 // NaN null + 1 // 1 undefined + 1 // NaN

3.4 == 和 ===有什麼區別,分別在什麼情況使用?

  1. ==等於操作符
  2. ===全等操作符

== 在比較時會進行隱式的型別轉換 ```js '' == '0' // false 0 == '' // true 0 == '0' // true

false == 'false' // false false == '0' // true

false == undefined // false false == null // false null == undefined // true

' \t\r\n' == 0 // true ```

比較 null 的情況的時候,我們一般使用相等操作符 ==

```js const obj = {};

if(obj.x == null){ console.log("1"); //執行 } ```

等同於下面寫法 js if(obj.x === null || obj.x === undefined) { ... }

使用相等操作符 == 的寫法明顯更加簡潔了

所以,除了在比較物件屬性為 null 或者 undefined 的情況下,我們可以使用相等操作符 ==,其他情況建議一律使用全等操作符 ===

3.5 let、var、const 的區別

letconst 都是 ES6 新增加了兩個重要的關鍵字,varES6 之前就有的, 他們都是用來宣告變數的,const 是用來宣告一個只讀的常量。

varletconst 三者區別可以圍繞下面五點展開: 1. 變數提升:var 可以在宣告之前呼叫,letconst 不行(會報錯) 2. 塊級作用域var 不存在跨級作用於,letconst 有 3. 重複宣告:var 允許重複宣告變數,letconst 不行 4. 修改宣告的變數:varlet 可以,const 不行 5. 使用:能用 const 的情況儘量使用 const,其他情況下大多數使用 let,避免使用 var

3.6 const 聲明瞭陣列,還能 push 元素嗎,為什麼?

可以

陣列是引用型別,const 宣告的引用型別變數,不可以變的是變數引用始終指向某個物件,不能指向其他物件,但是所指向的某個物件本身是可以變的

3.7 0.1 + 0.2 == 0.3?

答案是 false 不相等。

原因:0.10.2 在轉換成二進位制後會無限迴圈,由於標準位數的限制後面多餘的位數會被截掉,此時就已經出現了精度的損失,相加後因浮點數小數位的限制而截斷的二進位制數字在轉換為十進位制就會變成 0.30000000000000004

總結一句話就是:二進位制模擬十進位制進行計算時的精度問題

3.8 值型別 和 引用型別 的值得計算

1. 第一道 ```js var a = {"x": 1}; var b = a; a.x = 2; console.log(b.x);

a = {"x": 3}; console.log(b.x); a.x = 4; console.log(b.x); ```

答案放在了下面,思考一下哦~

原理: 1. 首先:第一個 log 列印 2 大家應該都沒問題 image.png

  1. 然後 a 指向了棧記憶體中的另一塊地址,而 b 沒變,所以 b.x 仍然為 2

image.png

  1. 接著修改 a.x = 4 ,因為此時 b 仍然指向之前的地址,所以修改 a.x 並不會去影響 b,所以列印仍然為 2

image.png

答案:2 2 2

2. 第二道:

```js var a = {n:1}; var b = a; a.x = a = {n:2};

console.log(a.x); console.log(b.x); ```

答案在下面思考下哦~

這次我嘗試一張圖解釋:

image.png

答案: undefined {n: 2}

4. JS 基礎-原型和原型鏈

三座大山之一,必考!!!

4.1 JavaScript 中的原型,原型鏈分別是什麼?

在 JS 中,原型和原型鏈是一個很重要的概念,可以說原型本質就是一個物件。它分為兩種: 物件的原型函式的原型

物件的原型: - 任何物件都有自己預設的原型(隱式原型), - 它的作用就是在當前物件查詢某一個屬性時, 如果找不到, 會在原型上面查詢 - 獲取隱式原型的方法: - __proto__(這個不是規範,是瀏覽器加的,因為早期沒有獲取原型物件的方法) - Object.getPrototypeOf(obj)

函式的原型: - 首先,因為函式也是一個物件,所以他會有一個 __proto__ 隱示原型 - 其次任何一個函式(非箭頭函式)還會有自己的 prototype 屬性(顯式原型) - 獲取顯示原型的方法: - prototype - 作用: - 當通過 new 操作符呼叫函式時, 建立一個新的物件 - 這個新的物件的 隱式原型 會指向這個函式的 顯式原型 - obj.__proto__ = F.prototype

原型鏈:

上面講過,隱式原型的作用是 當前物件查詢某一個屬性時, 如果找不到, 會在原型上面查詢,而原型也是一個物件,所以在原型物件上找不到的話,還會去原型物件的原型物件上找,這樣一層一層、以此類推就形成了原型鏈(prototype chain

4.2 如何準確判斷一個變數不是陣列?

1. instanceof

instanceof 運算子用於檢測建構函式的 prototype 屬性是否出現在某個例項物件的原型鏈上

如果讓你實現一個 instanceof 應該就很簡單了吧?(迴圈遍歷物件的隱式原型知道為 null 或者為 Arrayjs let arr = [1, 2]; arr instanceof Array // true

2. toString

js let arr = [1, 2]; Object.prototype.toString.call(arr) === '[object Array]' 3. constructor

js let arr = [1,2]; arr.constructor === Array; // true

4. isArray

js let arr = [1,2]; Array.isArray(arr) // true

4.3 JS 如何實現繼承

ES6 中可以使用 class + extends 的方式很容易實現繼承,而它的本質其實就是通過原型鏈來實現的

ES6 實現繼承:

```js class Person { constructor(name) { this.name = name; }

sayHello() { console.log("你好,我是" + this.name); } }

class Student extends Person { constructor(name, sno) { super(name); this.sno = sno; }

readingBooks() { console.log("我正在讀書"); } }

const stu = new Student("張三", "001");

// Student 子類能呼叫父類的 sayHello 方法 stu.sayHello(); stu.readingBooks(); ```

ES5 實現繼承: ``` function Person(name) { this.name = name; }

Person.prototype.sayHello = function () { console.log("你好,我是", this.name); };

function Student(name, sno) { this.name = name; this.sno = sno; }

const per = new Person() Student.prototype = perper;

Student.prototype.readingBooks = function () { console.log("我正在讀書"); };

const stu = new Student("張三", "001"); // Student 子類能呼叫父類的 sayHello 方法 stu.sayHello(); stu.readingBooks(); ``ES5中的繼承還會複雜一些,初級面知道這些就夠了,如果感興趣,還可以看下我的這篇文章 [js 常見手寫題彙總](http://yejiwei.com/post/85) 第14` 章實現繼承。

下面我們用一張圖來解釋原型鏈繼承的原理:

image.png

5. JS 基礎-作用域和閉包

三座大山之二,不會閉包,基本不會通過

5.1 什麼是作用域?什麼是自由變數?

作用域 就是即變數(變數作用域又稱上下文)和函式生效(能被訪問)的區域或集合。

我們一般將作用域分成:

  1. 全域性作用域:任何不在函式中或是大括號中宣告的變數,都是在全域性作用域下,全域性作用域下宣告的變數可以在程式的任意位置訪問
  2. 函式作用域:函式作用域也叫區域性作用域,如果一個變數是在函式內部宣告的它就在一個函式作用域下面。這些變數只能在函式內部訪問,不能在函式以外去訪問
  3. 塊級作用域:ES6 引入了 letconst 關鍵字,和 var 關鍵字不同,在大括號中使用 letconst 宣告的變數存在於塊級作用域中。在大括號之外不能訪問這些變數。

自由變數 就是一個變數在當前作用域沒有定義,但被使用了。那這個時候怎麼辦呢?它會向上級作用域,一層一層依次尋找,知道找到為止。如果到全域性作用域都沒找到,則報錯 xx is not defined

5.2 什麼是閉包?閉包會用在哪兒?

閉包是什麼?

一個函式和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一起(或者說函式被引用包圍),這樣的組合就是閉包(closure

也就是說,閉包讓你可以在一個內層函式中訪問到其外層函式的作用域

JavaScript 中,每當建立一個函式,閉包就會在函式建立的同時被創建出來,作為函式內部與外部連線起來的一座橋樑

下面給出一個簡單的例子: js function init() { var name = "Mozilla"; // name 是一個被 init 建立的區域性變數 function displayName() { // displayName() 是內部函式,一個閉包 alert(name); // 使用了父函式中宣告的變數 } displayName(); } init();

使用場景

閉包常常作為函式 引數返回值

  1. 作為函式引數:

```js function print(fn) { let a = 200; fn(); }

let a = 100; function fn() { console.log(a); }

print(fn); // 100 ``` 2. 作為函式的返回值

```js function create() { let a = 100; return function () { console.log(a); }; }

let fn = create(); let a = 200;

fn(); // 100 ```

任何閉包的使用場景都離不開這兩點: 1. 建立私有變數 2. 延長變數的生命週期

一般函式的詞法環境在函式返回後就被銷燬,但是閉包會儲存對建立時所在詞法環境的引用,即便建立時所在的執行上下文被銷燬,但建立時所在詞法環境依然存在,以達到延長變數的生命週期的目的

使用閉包的注意點

  1. 由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在 IE 中可能導致記憶體洩露。解決方法是,在退出函式之前,將不使用的區域性變數全部刪除。

  2. 閉包會在父函式外部,改變父函式內部變數的值。所以,如果你把父函式當作物件(object)使用,把閉包當作它的公用方法(Public Method),把內部變數當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函式內部變數的值。

5.3 this 的繫結規則有哪些?優先順序?

this 關鍵字是函式執行時自動生成的一個內部物件,只能在函式內部使用,總指向呼叫它的物件。

根據不同的使用場合,this 有不同的值,主要分為下面幾種情況:

1. 預設繫結:

什麼情況下使用預設繫結呢?獨立函式呼叫。

  • 獨立的函式呼叫我們可以理解成函式沒有被繫結到某個物件上進行呼叫;

案例一:普通函式呼叫

  • 該函式直接被呼叫,並沒有進行任何的物件關聯;
  • 這種獨立的函式呼叫會使用預設繫結,通常預設繫結時,函式中的 this 指向全域性物件(window); ```js function foo() { console.log(this); // window }

foo(); ```

案例二:函式呼叫鏈(一個函式又呼叫另外一個函式)

  • 所有的函式呼叫都沒有被繫結到某個物件上; ```js // 2.案例二: function test1() { console.log(this); // window test2(); }

function test2() { console.log(this); // window test3() }

function test3() { console.log(this); // window } test1(); ```

案例三:將函式作為引數,傳入到另一個函式中 ```js function foo(func) { func() }

function bar() { console.log(this); // window }

foo(bar); ```

我們對案例進行一些修改,考慮一下列印結果是否會發生變化:

  • 這裡的結果依然是 window,為什麼呢?
  • 原因非常簡單,在真正函式呼叫的位置,並沒有進行任何的物件繫結,只是一個獨立函式的呼叫;

```js function foo(func) { func() }

var obj = { name: "why", bar: function() { console.log(this); // window } }

foo(obj.bar); ``` 2. 隱式繫結:

另外一種比較常見的呼叫方式是通過某個物件進行呼叫的: - 也就是它的呼叫位置中,是通過某個物件發起的函式呼叫。

案例一:通過物件呼叫函式 - foo 的呼叫位置是 obj.foo() 方式進行呼叫的 - 那麼 foo 呼叫時 this 會隱式的被繫結到 obj 物件上 ```js function foo() { console.log(this); // obj物件 }

var obj = { name: "why", foo: foo }

obj.foo(); ```

案例二:案例一的變化

  • 我們通過 obj2 又引用了 obj1 物件,再通過 obj1 物件呼叫 foo 函式;
  • 那麼 foo 呼叫的位置上其實還是 obj1 被綁定了 this

```js function foo() { console.log(this); // obj1 物件 }

var obj1 = { name: "obj1", foo: foo }

var obj2 = { name: "obj2", obj1: obj1 }

obj2.obj1.foo(); ```

案例三:隱式丟失

結果最終是 window,為什麼是 window 呢? - 因為 foo 最終被呼叫的位置是 bar ,而 bar 在進行呼叫時沒有繫結任何的物件,也就沒有形成隱式繫結; - 相當於是一種預設繫結; ```js function foo() { console.log(this); }

var obj1 = { name: "obj1", foo: foo }

// 講obj1的foo賦值給bar var bar = obj1.foo; bar(); ``` 3. 顯示繫結

隱式繫結有一個前提條件:

  • 必須在呼叫的 物件內部 有一個對函式的引用(比如一個屬性);
  • 如果沒有這樣的引用,在進行呼叫時,會報找不到該函式的錯誤;
  • 正是通過這個引用,間接的將this繫結到了這個物件上;

如果我們不希望在 物件內部 包含這個函式的引用,同時又希望在這個物件上進行強制呼叫,該怎麼做呢?

  • JavaScript 所有的函式都可以使用 callapply 方法(這個和 Prototype 有關)。
    • 它們兩個的區別這裡不再展開;
    • 其實非常簡單,第一個引數是相同的,後面的引數,apply 為陣列,call 為引數列表;
  • 這兩個函式的第一個引數都要求是一個物件,這個物件的作用是什麼呢?就是給 this 準備的。
  • 在呼叫這個函式時,會將 this 繫結到這個傳入的物件上。

因為上面的過程,我們明確的綁定了 this 指向的物件,所以稱之為 顯示繫結

案例一callapply

通過 call 或者 apply 繫結 this 物件

  • 顯示繫結後,this 就會明確的指向繫結的物件 ```js function foo() { console.log(this); }

foo.call(window); // window foo.call({name: "why"}); // {name: "why"} foo.call(123); // Number物件,存放 123 ```

案例二bind 函式

如果我們希望一個函式總是顯示的繫結到一個物件上,我們可以使用 bind 函式: ``` function foo() { console.log(this); }

var obj = { name: "why" }

var bar = foo.bind(obj);

bar(); // obj物件 bar(); // obj物件 bar(); // obj物件 ```

案例三:內建函式

有些時候,我們會呼叫一些 JavaScript 的內建函式,或者一些第三方庫中的內建函式。

  • 這些內建函式會要求我們傳入另外一個函式;
  • 我們自己並不會顯示的呼叫這些函式,而且 JavaScript 內部或者第三方庫內部會幫助我們執行;
  • 這些函式中的 this 又是如何繫結的呢?

```js // 1. setTimeout中會傳入一個函式,這個函式中的this通常是window setTimeout(function() { console.log(this); // window }, 1000);

// 2. forEach map filter 等高階函式 this 通常指向 window物件,但我們可以通過第二個引數改變 var names = ["abc", "cba", "nba"]; names.forEach(function(item) { console.log(this); // 三次window });

var names = ["abc", "cba", "nba"]; var obj = {name: "why"}; names.forEach(function(item) { console.log(this); // 三次obj物件 }, obj); ```

4. new 繫結

JavaScript 中的函式可以當做一個類的建構函式來使用,也就是使用 new 關鍵字。

使用 new 關鍵字來呼叫函式時,會執行如下的操作:

1.建立一個全新的物件; 2.這個新物件會被執行 Prototype 連線; 3.這個新物件會繫結到函式呼叫的 thisthis 的繫結在這個步驟完成); 4.如果函式沒有返回其他物件,表示式會返回這個新物件;

```js // 建立Person function Person(name) { console.log(this); // Person {} this.name = name; // Person {name: "why"} }

var p = new Person("why"); console.log(p); ```

5. 優先順序

new繫結 > 顯示繫結(bind) > 隱式繫結 > 預設繫結

PS: new 繫結後可以使用 bind 但是 bind 不會生效。 new 繫結後使用 callapply 會報錯。

5. 規則之外 - bind 繫結一個 null 或者 undefined 無效 - 間接函式引用

```js function foo() { console.log(this); }

var obj1 = { name: "obj1", foo: foo };

var obj2 = { name: "obj2" }

obj1.foo(); // obj1物件

// 賦值(obj2.foo = obj1.foo)的結果是foo函式 // foo函式被直接呼叫,那麼是預設繫結; (obj2.foo = obj1.foo)(); // window `` -ES6箭頭函式:箭頭函式不使用this的四種標準規則(也就是不繫結this),而是根據外層作用域來決定this`。

6. JS 基礎-非同步

三座大山之三,必考!!!

6.1 同步 和 非同步 的區別

JS 是一門單執行緒的程式語言,這就意味著一個時間裡只能處理一件事,也就是說 JS 引擎一次只能在一個執行緒裡處理一條語句。(瀏覽器和 nodejs 已經支援 js 啟動程序,如 web worker

雖然單執行緒簡化了程式設計程式碼,因為這樣咱們不必太擔心併發引出的問題,這也意味著在阻塞主執行緒的情況下執行長時間的操作,如網路請求。

想象一下從 API 請求一些資料,根據具體的情況,伺服器需要一些時間來處理請求,同時阻塞主執行緒,使網頁長時間處於無響應的狀態。這就是引入非同步 JS 的原因。使用非同步 (如 回撥函式、promiseasync/await),可以不用阻塞主執行緒的情況下長時間執行網路請求。

總結:基於 js 單執行緒本質,同步會阻塞程式碼執行,非同步不會阻塞程式碼執行。

6.2 非同步的應用場景

  • 網路請求,如 ajax 載入圖片
  • 定時任務,setTimeout

6.3 你是怎麼理解ES6中 Promise的?

1. 介紹

Promise,譯為承諾,是非同步程式設計的一種解決方案,比傳統的解決方案(回撥函式)更加合理和更加強大。

Promise 的出現主要解決了用回撥函式處理多層非同步操作出現的回撥地獄問題

```js // 1. 經典的回撥地獄 doSomething(function(result) { doSomethingElse(result, function(newResult) { doThirdThing(newResult, function(finalResult) { console.log('得到最終結果: ' + finalResult); }, failureCallback); }, failureCallback); }, failureCallback);

// 2. 鏈式操作減低了編碼難度 程式碼可讀性明顯增強 doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('得到最終結果: ' + finalResult); }) .catch(failureCallback); ```

promise 物件僅有三種狀態:pending(進行中) fulfilled(已成功) rejected(已失敗)

  • 物件的狀態不受外界影響,只有非同步操作的結果,可以決定當前是哪一種狀態
  • 一旦狀態改變(從 pending 變為 fulfilled 和從 pending 變為 rejected ),就不會再變,任何時候都可以得到這個結果

image.png

2. 用法

建立一個 Promise js // 1. Promise物件是一個建構函式,用來生成Promise例項 // resolve函式的作用是,將Promise物件的狀態從“未完成”變為“成功” // reject函式的作用是,將Promise物件的狀態從“未完成”變為“失敗” const promise = new Promise(function(resolve, reject) {});

例項方法: - then()then 是例項狀態發生改變時的回撥函式,第一個引數是 resolved 狀態的回撥函式,第二個引數是 rejected 狀態的回撥函式 - catch()catch() 方法是 .then(null, rejection).then(undefined, rejection) 的別名,用於指定發生錯誤時的回撥函式 - finally()finally() 方法用於指定不管 Promise 物件最後狀態如何,都會執行的操作

建構函式方法:

  • all()
  • race()
  • allSettled()
  • resolve():將現有物件轉為 Promise 物件
  • reject():返回一個新的 Promise 例項,該例項的狀態為rejected

ps:all race allSettled 這三個方法都能將多個 promise例項轉換為 1promise 例項,區別就是 all 只有當所有例項狀態都變為 fulfilled 最終返回的例項狀態才會變為 fulfilledrace 則返回最快改變狀態的例項,allSettled 則會等所有例項狀態改變才會改變。

6.4 手寫 Promise 載入一張圖片

js const preloadImage = function (path) { return new Promise(function (resolve, reject) { const image = new Image(); image.onload = () => { resolve(image) }; image.onerror = () => { const err = new Error(`圖片載入失敗${path}`) reject(err) }; image.src = path; }); };

7. JS 非同步進階

關於非同步還有更多的問題,很重要

7.1 什麼是事件迴圈 event loop?

  1. 因為 js 是單執行緒的,為了防止程式碼阻塞會將程式碼分成同步非同步
  2. 同步程式碼交給 js 引擎執行,非同步程式碼交給宿主環境(瀏覽器、node)
  3. 同步程式碼放入執行棧中,非同步程式碼等待時機成熟送入任務佇列排隊
  4. 執行棧執行完畢,會去任務佇列看是否有非同步任務,有就送到執行棧執行,反覆迴圈檢視執行程式碼,這個過程就是事件迴圈(event loop

7.2 什麼是巨集任務和微任務,它們的區別?

上題講到 js 將程式碼分成同步和非同步,而在非同步任務中又分 巨集任務(macro-task)和微任務(micro-task)。 巨集任務是由宿主(瀏覽器、node)發起的,微任務是由 js 引擎發起的

巨集任務大概包括 1. script(整體程式碼) 2. setTimeout 3. setInterval 4. setImmediate 5. I/O 6. UI render

微任務大概包括 1. process.nextTick 2. Promise 3. Async/Await(實際就是promise) 4. MutationObserver(html5新特性)

巨集任務和微任務的執行過程 1. 先執行同步程式碼 2. 後執行微任務的非同步程式碼 3. 再執行巨集任務的非同步程式碼

如下圖所示:

image.png

下面有個小例子: ```js setTimeout(() => { console.log(1); });

Promise.resolve().then(() => { console.log(2); }); console.log(3); ```

程式碼最終執行: 3 2 1

考非同步程式碼執行順序的題目有很多,這裡推薦一個 練習事件迴圈的網站

7.3 async/await 語法

用法很簡單,這裡不說了。

你可以把 await 後面的程式碼理解為是放在 Promise.then 中,看上去相當於將鏈式的呼叫變成了同步的執行

關於async/await 的本質 最好去理解一下,其實就是 PromiseGenerator 的語法糖

7.4 手寫 Promise

詳見 JavaScript 常見手寫題彙總15

8. JS-Web-API-DOM

學會DOM,才能具備網頁開發的基礎

8.1 DOM 是什麼?

DOM:Document Object Model 指的是文件物件模型,它指的是把文件當做一個物件,這個物件主要定義了處理網頁內容的方法和介面。

DOM 的本質就是一顆樹

8.2 DOM 節點的操作

獲取 DOM 節點 1. getElementById 2. getElementByClassName 3. getElementByTagName 4. querySeletorAll

DOM 節點的 property

通過 js 物件屬性的方式來獲取或修改 DOM 節點 ```js const pList = document.querySelectorAll('p') const p = pList[0] console.log(p.style.width) // 獲取樣式 p.style.width = '100px' // 修改樣式 console.log(p.className) // 獲取 class p.className = 'p1' // 修改 class

// 獲取 nodeName 和 nodeType

console.log(p.nodeName) console.log(p.nodeType) ```

DOM 節點的 attribute

通過 getAttribute setAttribute 這種 API 的方式來修改 DOM 節點

```js const pList = document.querySelectorAll('p') const p = pList[0]

p.setAttribute('data-name', 'coder') // 設定值 console.log(p.getAttribute('data-name')) // 獲取值 ```

propertyattribute 形式都可以修改節點的屬性,但是對於新增或刪除的自定義屬性,能在 htmldom 樹結構上體現出來的,就必須要用到 attribute 形式了。

8.3 DOM 結構操作

新增插入節點:

```html

div1
div2

這是 p2 標籤

```

獲取子元素列表,獲取父元素 js // 獲取父元素 console.log(p1.parentNode); // 獲取子元素列表 console.log(div2.childNodes);

刪除子元素 js // 刪除子元素 div2.removeChild(div2.childNodes[0]);

8.4 如何優化 DOM 效能

DOM 操作非常昂貴,避免頻繁的 DOM 操作 - 對 DOM 操作做快取 - 將頻繁 DOM 操作改為一次性操作

9. JS-Web-API-BOM

內容雖然不多,但是你不能不會

9.1 什麼 BOM ?

BOM:Browser Object Model 指的是瀏覽器物件模型,它指的是把瀏覽器當做一個物件來對待,這個物件主要定義了與瀏覽器進行互動的法和介面。BOM 的核心是 window,而 window 物件具有雙重角色,它既是通過 js 訪問瀏覽器視窗的一個介面,又是一個 Global(全域性)物件。這意味著在網頁中定義的任何物件,變數和函式,都作為全域性物件的一個屬性或者方法存在。window 物件含有 location 物件navigator 物件screen 物件等子物件,並且 DOM 的最根本的物件 document 物件也是 BOMwindow 物件的子物件。

9.2 如何識別瀏覽器型別

navigator.userAgent 簡稱 ua,可以從 ua 裡拿到瀏覽器資訊

9.3 拆解 url 各部分

location: 1. href 2. protocol 3. pathname 4. search 5. hash

10. JS-Web-API-事件

事件不會,等於殘廢,必考!必考!

10.1 談談你對事件冒泡和捕獲的理解

事件冒泡和事件捕獲分別由微軟和網景公司提出,這兩個概念都是為了解決頁面中事件流(事件發生順序)的問題。

```html

Click me!

```

上面的的兩個元素,如果點選 inner,他們的執行順序是什麼呢? - 事件冒泡:先執行 inner 的監聽事件在執行 outer 的監聽事件 - 事件捕獲:先執行 outer 的監聽事件在執行 inner 的監聽事件

其實這個很好理解,冒泡就是從一個泡泡從水底往上冒當然是裡面的先執行啦。

至於為什麼會有這兩種情況,這就要談到網景和微軟的戰爭了,兩家公司的理念不同。網景主張捕獲方式,微軟主張冒泡方式。後來 w3c 將兩者都保留了下來。

addEventListener 的第三個引數就是為冒泡和捕獲準備的。第三個引數設定為 true 可以將讓當前元素繫結的事件先於裡面的元素繫結事件執行。預設是 false

10.2 什麼是 事件代理

事件代理(Event Delegation)也稱之為事件委託。是 JavaScript 中常用繫結事件的常用技巧。

顧名思義,事件代理 即是把原本需要繫結在子元素的響應事件委託給父元素,讓父元素擔當事件監聽的職務。

事件代理的原理是DOM元素的事件冒泡。

11. JS-Web-API-Ajax

每個工程師必須熟練掌握的技能

11.1 什麼是 Ajax ?

Ajax(全稱 Asynchronous JavaScript And XML) 翻譯過來就是 非同步的 Javascript 和 XML

AJAX 是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。

隨著谷歌搜尋建議功能在 2005 的釋出,AJAX 開始流行起來。

11.2 手寫一個簡易的 Ajax

網頁中實現 Ajax 最核心的 API 就是 XMLHttpRequest,如果不知道這個就別談實現了。

js function ajax(url) { const xhr = new XMLHttpRequest(); xhr.open("get", url, true); xhr.onreadystatechange = function () { // 非同步回撥函式 if (xhr.readyState === 4) { if (xhr.status === 200) { console.info("響應結果", xhr.response) } } } xhr.send(null); }

11.3 瀏覽器的同源策略是什麼??

同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說 Web 是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。

它的核心就在於它認為自任何站點裝載的信賴內容是不安全的。當被瀏覽器半信半疑的指令碼執行在沙箱時,它們應該只被允許訪問來自同一站點的資源,而不是那些來自其它站點可能懷有惡意的資源。

所謂同源是指:域名、協議、埠相同。

另外,同源策略又分為以下兩種:

  • DOM 同源策略:禁止對不同源頁面 DOM 進行操作。這裡主要場景是 iframe 跨域的情況,不同域名的 iframe 是限制互相訪問的。
  • XMLHttpRequest 同源策略:禁止使用 XHR 物件向不同源的伺服器地址發起 HTTP 請求。

11.4 什麼是跨域?

跨域本質是瀏覽器基於同源策略的一種安全手段

同源策略(Sameoriginpolicy),是一種約定,它是瀏覽器最核心也最基本的安全功能

所謂同源(即指在同一個域)具有以下三個相同點

  1. 協議相同(protocol)
  2. 主機相同(host)
  3. 埠相同(port)

反之非同源請求,也就是協議、埠、主機其中一項不相同的時候,這時候就會產生跨域

一定要注意跨域是瀏覽器的限制,你用抓包工具抓取介面資料,是可以看到介面已經把資料返回回來了,只是瀏覽器的限制,你獲取不到資料。用 postman 請求介面能夠請求到資料。這些再次印證了跨域是瀏覽器的限制。

11.5 如何實現跨域請求

首先跨域是因為瀏覽器的同源策略造成的,他是瀏覽器的一種安全機制。跨域並不是請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了

實現跨域常見的方案:

  1. jsonp
  2. nginx反向代理
  3. webpack devServer代理
  4. cors跨源資源共享

其中 jsonp 由於其侷限性,以及對比其他方案的效果。此處不做介紹。

1. nginx方向代理

nginx 反向代理常用在開發環境及線上環境。通過攔截轉發請求來處理跨域問題。

假如現在前端專案執行在 8080 埠,而實際後端專案的地址為 https://1.1.1.1:9000 ,需要攔截字首為 api 的請求,此時 nginx 配置為:

sh server { listen 8080 default_server; location /api { proxy_pass https://1.1.1.1:9000; } }

假如現在有個介面為 /api/test,在沒有做轉發前為 http://localhost:8080/api/test ,實際介面位置為 https://1.1.1.1:9000/api/test.結果轉發為實際介面位置。

2. webpack devserver代理 webpack devserver 代理用在開發環境。

配置如下: js devServer({ proxy: { '/api': { target: 'https://1.1.1.1:9000', changeOrigin: true, pathRewrite: { '^/api': 'api' }, }, } })

3. cors跨源資源共享(服務端設定)

跨源資源共享 (CORS) (或通俗地譯為跨域資源共享)是一種基於 HTTP 頭的機制,該機制通過允許伺服器標示除了它自己以外的其它 origin(域,協議和埠),這樣瀏覽器可以訪問載入這些資源。

Access-Control-Allow-Origin: 'xxx'

可以通過服務端設定 Access-Control-Allow-Origin 欄位的白名單來處理跨域問題。

如果在此情況下,傳送請求時需要帶上cookie的話,則需要配置Access-Control-Allow-Credentials,同時客戶端需要同步設定xhr.withCredentials = true;,兩者缺一不可

11.6 ajax fetch axios 的區別

  1. ajaxjs 非同步技術的術語,早期相關的 apixhr ,它是一個術語。
  2. fetches6 新增的用於網路請求標準 api,它是一個 api。(是 es6 用來代替 xhr 的, xhr 很不好用)
  3. axios 是用於網路請求的第三方庫,它是一個庫。

12. JS-Web-API-儲存

內容雖然不多,但不可不會

12.1 描述 cookie sessionStorage localStorage 的區別

cookie 本身是用於瀏覽器和 server 通訊的,他是被借用到本地用於儲存的,因為後兩者是在 H5 後才提出來的( 2010年左右),我們可以通過 document.cookie = 'xxx' 來改變 cookie

cookie 的缺點: 1. 儲存大小最大 4kb (因為 cookie 本身就不是用來做儲存的) 2. http 請求時需要傳送到服務端,增加請求資料量 3. 只能用 document.cookie 來修改,太過簡陋

localStoragesessionStorage

  1. 它倆是 H5 專門為了儲存設計的,最大可存 5M 左右
  2. API 更簡潔:getItem setItem
  3. 不會隨著 http 被髮送出去
  4. localStorage 資料會永久儲存,除非程式碼或者手動刪除, sessionStorage 資料只存在於房錢會話,瀏覽器關閉則清除。一般 localStorage 會更多一些

13. HTTP 面試題

前後端分離的時代,網路請求是前端的生命線

13.1 http 常見的狀態碼有哪些?

分類: 1. 1xx 伺服器收到請求 2. 2xx 請求成功,如 200 3. 3XX 重定向,如 302 4. 4xx 客戶端錯誤,如 404 5. 5xx 服務端錯誤,如 500

常見狀態碼:

  • 1XX: 提供資訊
  • 100 Continue 情景:客戶端向服務端傳很大的資料,這個時候詢問服務端,如果服務端返回100,客戶端就繼續傳 (歷史,現在比較少了)
  • 101 Switching Protocols 協議切換。比如下面這種:

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade 告訴客戶端把協議切換為 Websocket - 2xx: 成功 - 200 Ok 正常的返回成功 通常用在 GET - 201 Created 已建立 通常用在 POST - 202 Accepted 已接收 比如傳送一個建立 POST 請求,服務端有些非同步的操作不能馬上處理先返回 202,結果需要等通知或者客戶端輪詢獲取 - 203 Non-Authoritative Infomation 非權威內容 原始伺服器的內容被修改過 - 204 No Content 沒有內容 一般 PUT 請求修改了但是沒有返回內容 - 205 Reset Content 重置內容 - 206 Partial Content 服務端下發了部分內容 - 3XX: 重定向 - 300 Multiple Choices 使用者請求了多個選項的資源(返回選項列表) - 301 Moved Permanently 永久轉移 - 302 Found 資源被找到(以前是臨時轉移)不推薦用了 302 拆成了 303307 - 303 See Other 可以使用 GET 方法在另一個 URL 找到資源 - 304 Not Modified 沒有修改 - 305 Use Proxy 需要代理 - 307 Temporary Redirect 臨時重定向 (和 303 的區別是,307 使用原請求的method 重定向資源, 303 使用 GET 方法重定向資源) - 308 Permanent Redirect 永久重定向 (和 301 區別是 客戶端接收到 308 後,之前是什麼 method,之後也會沿用這個 method 到新地址。301,通常給使用者會向新地址傳送 GET 請求) - 4XX: 客戶端錯誤 - 400 Bad Request 請求格式錯誤 - 401 Unauthorized 沒有授權 - 402 Payment Required 請先付費 - 403 Forbidden 禁止訪問 - 404 Not Found 沒有找到 - 405 Method Not Allowed 方法不允許 - 406 Not Acceptable 服務端可以提供的內容和客戶端期待的不一樣 - 5XX: 服務端錯誤 - 500 Internal Server Error 內部伺服器錯誤 - 501 Not Implemented 沒有實現 - 502 Bad Gateway 閘道器錯誤 - 503 Service Unavailable 服務不可用 (記憶體用光了,執行緒池溢位,服務正在啟動) - 504 Gateway Timeout 閘道器超時 - 505 HTTP Version Not Supported 版本不支援

面試的時候常見該記住的有:101 200 201 301 302 304 403 404 500 502 504

規範就是一個約定,要求大家跟著執行,不要違反規範,例如 IE 瀏覽器

13.2 http 常見的 header 頭

1. Content-Length

  • 傳送給接收者的 Body 內容長度(位元組)

    • 一個 byte 是 8bit
    • UTF-8 編碼的字元 1-4 個位元組、

示例:Content-Length: 348

2. User-Agent

  • 幫助區分客戶端特性的字串

    • 作業系統
    • 瀏覽器
    • 製造商(手機型別等)
    • 核心型別
    • 版本號

示例:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36

3. Content-Type

  • 幫助區分資源的媒體型別(Media Type/MIME Type)

    • text/html
    • text/css
    • application/json
    • image/jpeg

示例:Content-Type: application/x-www-form-urlencoded

4. Origin

  • 描述請求來源地址

  • scheme://host:port

  • 不含路徑
  • 可以是null

示例: Origin: https://yewjiwei.com

5. Accept

  • 建議服務端返回何種媒體型別(MIME Type)

    • /代表所有型別(預設)
    • 多個型別用逗號隔開
    • 衍生的還有
      • Accept-Charset 能夠接受的字符集 示例:Accept-Charset: utf-8
      • Accept-Encoding 能夠接受的編碼方式列表 示例:Accept-Encoding: gzip, deflate
      • Accept-Language 能夠接受的迴應內容的自然語言列表 示例:Accept-Language: en-US

示例: 1. Accept: text/plain 2. Accept-Charset: utf-8 3. Accept-Encoding: gzip, deflate

6. Referer

  • 告訴服務端開啟當前頁面的上一張頁面的URL;如果是ajax請求那麼就告訴服務端傳送請求的URL是什麼

  • 非瀏覽器環境有時候不傳送Referer

  • 常常使用者行為分析

7. Connection

  • 決定連線是否在當前事務完成後關閉

    • HTTP1.0預設是 close
    • HTTP1.1後預設是 keep-alive

13.3 什麼是 RESTFUL API ?

Restful API 是一種新的 API 設計方法(早已推廣)

  • 傳統 API 設計:把每一個 url 當做一個功能
  • Restful API 設計:把每個 url 當做一個唯一的資源

傳統 /api/list?pageIndex=2

Restful API /api/list/2

13.4 描述一下 http 快取機制

HTTP 快取即是瀏覽器第一次向一個伺服器發起 HTTP 請求後,伺服器會返回請求的資源,並且在響應頭中新增一些有關快取的欄位如:cache-controlexpires, last-modifedETag, Date,等,之後瀏覽器再向該伺服器請求資源就可以視情況使用強快取和協商快取, - 強快取:瀏覽器直接從本地快取中獲取資料,不與伺服器進行互動, - 協商快取:瀏覽器傳送請求到伺服器,伺服器判斷是否可使用本地快取,