【面試】前端JavaScript面試技巧

聲明:慕課網《前端JavaScript面試技巧》的筆記,僅用於查閱複習,不得用於商業用途。javascript

BAT大牛帶你橫掃初級前端JavaScript面試

第1章 課程簡介

1-1 課程簡介

基礎知識css

  • 原型、原型鏈
  • 做用域、閉包
  • 異步、單線程

JS APIhtml

  • DOM 操做
  • Ajax
  • 事件綁定

開發環境前端

  • 版本管理
  • 模塊化
  • 打包工具

運行環境java

  • 頁面渲染
  • 性能優化

1-2 前言

關於面試node

  • 基層工程師 - 基礎知識
  • 高級工程師 - 項目經驗
  • 架構師 - 解決方案

關於基礎jquery

  • 工程師的自我修養 - 基礎
  • 紮實的基礎會讓你高效學習新技術

1-3 幾個面試題

先從幾道面試題提及git

  • JS 中使用 typeof 能獲得的哪些類型?
  • 什麼時候使用 === ,什麼時候使用 ==
  • window.onloadDOMContentLoaded 的區別?
  • 用 JS 建立 10 個 <a> 標籤,點擊的時候彈出對應的序號
  • 簡述如何實現一個模塊加載器,實現相似 require.js 的基本功能
  • 實現數組的隨機排序

思考github

  • 拿到一個面試題,你第一時間看到的是什麼?
  • 又如何看待網上搜出來的永遠看不完的題海?
  • 如何對待接下來遇到的面試題?

1-4 如何搞定全部面試題

上一節思考問題的結論面試

  • 拿到一個面試題,你第一時間看到的是什麼?- 考點
  • 又如何看待網上搜出來的永遠看不完的題海?- 不變應萬變
  • 如何對待接下來遇到的面試題?- 題目到知識再到題目

題目考察的知識點

  • JS 中使用 typeof 能獲得的哪些類型? 考點:JS 變量類型
  • 什麼時候使用 === ,什麼時候使用 == ? 考點:強制類型轉換
  • window.onloadDOMContentLoaded 的區別? 考點:瀏覽器渲染過程
  • 用 JS 建立 10 個 <a> 標籤,點擊的時候彈出來對應的序號 考點:做用域
  • 簡述如何實現一個模塊加載器,實現相似 require.js 的基本功能 考點:JS 模塊化
  • 實現數組的隨機排序 考點:JS 基礎算法

第2章 JS 基礎知識(上)

2-1 變量類型和計算 - 1

題目

  • JS 中使用 typeof 能獲得的哪些類型?
  • 什麼時候使用 === ,什麼時候使用 ==
  • JS 中有哪些內置函數?
  • JS 變量按照存儲方式區分爲哪些類型,並描述其特色?
  • 如何理解 JSON ?

知識點

  • 變量類型
  • 變量計算

變量類型

  • 值類型、引用類型
  • typeof 運算符

值類型

let a = 100
let b = a
a = 200
console.log(b) // 100
複製代碼

引用類型

let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
複製代碼

typeof 運算符

typeof undefined // "undefined"
typeof 'abc' // "string"
typeof 123 // "number"
typeof true // "boolean"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
typeof console.log // "function"
複製代碼

變量計算 - 強制類型轉換

字符串拼接

let a = 100 + 10 // 110
let b = 100 + '10' // "10010"
複製代碼

== 運算符

100 == '100' // true
0 == '' // true
null = undefined // true
複製代碼

if 語句

let a = true
if (a) {}
let b = 100
if (b) {}
let c = ''
if (c) {}
複製代碼

邏輯運算符

console.log(10 && 0) // 0
console.log('' || 'abc') // "abc"
console.log(!window.abc) // true

// 判斷一個變量會被當作 true 仍是 false
let a = 100
console.log(!!a) // true
複製代碼

2-2 變量類型和計算 - 2

解答

JS 中使用 typeof 能獲得的哪些類型?

答:undefinedstringnumberbooleanobjectfunctionsymbol

let sym = Symbol('commet')
console.log(typeof sym) // "symbol"
複製代碼

什麼時候使用 === ,什麼時候使用 ==

答:判斷對象的某個屬性是否存在或爲 null 時可使用 == ,由於 jQuery 源碼這麼使用

if (obj.a == null) {
  // 這裏至關於 obj.a === null || obj.a === undefined 的簡寫形式
  // 這是 jQuery 源碼中推薦的寫法
}
複製代碼

JS 中有哪些內置函數?

答:數據封裝類對象,包括 StringNumberBooleanObjectArrayFunctionDateRegExpError

JS 變量按照存儲方式區分爲哪些類型,並描述其特色?

答:值類型和引用類型,一個存儲值,一個存儲引用地址,引用類型能夠無限拓展屬性

2-3 變量類型和計算 - 3

如何理解 JSON ?

答:JSON 只不過是一個 JS 對象而已,也是一種數據格式

JSON.stringify({ a: 10, b: 20 })
JSON.parse('{"a":10,"b":20}')
複製代碼

2-4 變量類型和計算 - 代碼演示

if 語句中條件爲 false 的狀況有哪些?

答:''0NaNfalsenullundefined

JS 中有哪些內置對象?

答:JSONMath

2-5 typeof symbol

let s = Symbol()
typeof s // "symbol"
let s1 = Symbol()
console.log(s === s1) // false
let s2 = Symbol('s2s2')
console.log(s2) // Symbol(s2s2)
let s3 = s2
console.log(s3 === s2) // true
let sym1 = Symbol('111')
let sym2 = Symbol('222')
let obj = { [sym1]: 'hello world' }
obj[sym2] = 123
console.log(obj) // {Symbol(111): "hello world", Symbol(222): 123}
console.log(obj[sym1]) // "hello world"
console.log(obj[sym2]) // 123
複製代碼

2-6 原型和原型鏈

題目

  • 如何準確判斷一個變量是數組類型
  • 寫一個原型鏈繼承的例子
  • 描述 new 一個對象的過程
  • zepto 或其餘框架源碼中如何使用原型鏈

知識點

  • 構造函數
  • 構造函數 - 擴展
  • 原型規則和示例
  • 原型鏈
  • instanceof

構造函數

function Foo(name, age) {
  this.name = name
  this.age = age
  this.class = 'class__1'
  // return this // 默認有這一行
}
let f = new Foo('negrochn', 18)
// let f2 = new Foo('lexiaodai', 17) // 建立多個對象
複製代碼

構造函數 - 擴展

let a = {} // 實際上是 let a = new Object() 的語法糖
let b = [] // 實際上是 let b = new Array() 的語法糖
function Foo() {} // 實際上是 let Foo = new Function()
// 使用 instanceof 判斷一個函數是不是一個變量的構造函數
console.log(b instanceof Array) // true
複製代碼

2-7 原型和原型鏈 - 5 條原型規則

5 條原型規則

  1. 全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性(除了 null 之外)
  2. 全部的引用類型(數組、對象、函數),都有一個 __proto__(隱式原型)屬性,屬性值是一個普通的對象
  3. 全部的函數,都有一個 prototype(顯式原型)屬性,屬性值也是一個普通的對象
  4. 全部的引用類型(數組、對象、函數),__proto__ 屬性值指向它的構造函數的 prototype 屬性值
  5. 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的 __proto__(即它的構造函數的 prototype)中尋找
// 原則 1
let obj = {}
obj.a = 100
let arr = []
arr.a = 100
function fn() {}
fn.a = 100

// 原則 2
console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)

// 原則 3
console.log(fn.prototype)

// 原則 4
console.log(obj.__proto__ === Object.prototype) // true
複製代碼
// 構造函數
function Foo(name) {
  this.name = name
}
Foo.prototype.alertName = function() {
  alert(this.name)
}

// 建立實例
let f = new Foo('negrochn')
f.printName = function() {
  console.log(this.name)
}
// 測試
f.printName()
f.alertName() // 原則 5
複製代碼

2-8 原型和原型鏈 - 5 條原型規則 補充 2 點

for (let key in f) {
  // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性
  // 可是這裏建議你們仍是加上這個判斷,保證程序的健壯性
  if (f.hasOwnProperty(key)) {
    console.log(key)
  }
}
複製代碼

2-9 原型和原型鏈 - 原型鏈

// 構造函數
function Foo(name) {
  this.name = name
}
Foo.prototype.alertName = function() {
  alert(this.name)
}

// 建立實例
let f = new Foo('negrochn')
f.printName = function() {
  console.log(this.name)
}
// 測試
f.printName()
f.alertName()
f.toString() // 要去 f.__proto__.__proto__ 中查找
複製代碼

原型鏈

2-10 原型和原型鏈 - instanceof

// instanceof 用於判斷引用類型屬於哪一個構造函數的方法
console.log(f instanceof Foo) // true, f 的 __proto__ 一層一層往上,可否對應到 Foo.prototype
console.log(f instanceof Object) // true
複製代碼

2-11 原型和原型鏈 - 解答 1

如何準確判斷一個變量是數組類型

答:使用 instanceof Array

let arr = []
console.log(arr instanceof Array) // true
console.log(typeof arr) // "object" ,typeof 是沒法判斷是不是數組的
複製代碼

寫一個原型鏈繼承的例子

// 動物
function Animal() {
  this.eat = function() {
    console.log('animal eat')
  }
}
// 狗
function Dog() {
  this.bark = function() {
    console.log('dog bark')
  }
}
Dog.prototype = new Animal()
// 哈士奇
let hashiqi = new Dog()

// 接下來代碼演示時,會推薦更加貼近實戰的原型繼承示例
複製代碼

描述 new 一個對象的過程

答:

  1. 建立一個新對象
  2. this 指向這個新對象
  3. 執行代碼,即對 this 賦值
  4. 返回 this
function Foo(name, age) {
  this.name = name
  this.age = age
  this.class = 'class__1'
  // return this // 默認有這一行
}

let f = new Foo('negrochn', 18)
複製代碼

zepto 或其餘框架源碼中如何使用原型鏈

答:

  • 閱讀源碼是高效提升技巧的方式
  • 但不能「埋頭苦鑽」,有技巧在其中
  • 慕課網搜索「zepto 設計和源碼分析」

2-12 原型和原型鏈 - 解答 2

寫一個封裝 DOM 查詢的例子

function Elem(id) {
  this.elem = document.getElementById(id)
}

Elem.prototype.html = function(val) {
  let elem = this.elem
  if (val) {
    elem.innerHTML = val
    return this // 爲了鏈式操做
  } else {
    return elem.innerHTML
  }
}

Elem.prototype.on = function(type, fn) {
  let elem = this.elem
  elem.addEventListener(type, fn)
  return this // 爲了鏈式操做
}

let div1 = new Elem('div1')
div1.html('<p>Hello World</p>').on('click', function() {
  alert('clicked')
})
複製代碼

2-13 原型和原型鏈 - 代碼演示

第3章 JS 基礎知識(中)

3-1 做用域和閉包 - 執行上下文

題目

  • 說一下對變量提高的理解
  • 說明 this 幾種不一樣的使用場景
  • 建立 10 個 <a> 標籤,點擊的時候彈出對應的序號
  • 如何理解做用域
  • 實際開發中閉包的應用

知識點

  • 執行上下文
  • this
  • 做用域
  • 做用域鏈
  • 閉包

執行上下文

  • 範圍:一段 <script> 或者一個函數
  • 全局上下文:執行前,會先把變量定義、函數聲明拿出來(注意函數聲明和函數表達式的區別)
  • 函數上下文:執行前,先把變量定義、函數聲明、thisarguments 拿出來
console.log(a) // undefined
var a = 100

fn('negrochn') // "negrochn" 20
function fn(name) {
  age = 20
  console.log(name, age)
  var age
}
複製代碼

3-2 做用域和閉包 - 執行上下文 代碼演示

3-3 做用域和閉包 - this

this ,要在執行時才能確認值,定義時沒法確認

  • 做爲構造函數執行
  • 做爲對象屬性執行
  • 做爲普通函數執行
  • callapplybind
var a = {
  name: 'A',
  fn: function() {
    console.log(this.name)
  }
}
a.fn() // this === a
a.fn.call({ name: 'B' }) // this 做爲 { name: 'B' }
var fn1 = a.fn
fn1() // this === window
複製代碼

3-4 做用域和閉包 - this 代碼演示

// 做爲構造函數執行
function Foo(name) {
  this.name = name
}
let f = new Foo('negrochn')
f.name // "negrochn"
複製代碼
// 做爲對象屬性執行
let obj = {
  name: 'A',
  printName: function() {
    console.log(this.name)
  }
}
obj.printName() // "A"
複製代碼
// 做爲普通函數執行
function fn() {
  console.log(this)
}
fn() // window
複製代碼
// call 、apply 、bind
function fn1(name, age) {
  console.log(name)
  console.log(this)
}
fn1.call({ x: 1 }, 'negrochn', 20) // "negrochn" { x: 1 }
fn1.apply({ x: 200 }, ['negrochn', 20]) // "negrochn" { x: 200 }

let fn2 = function(name, age) {
  console.log(name)
  console.log(this)
}.bind({ x: 300 })
fn2('negrochn', 20) // "negrochn" { x: 300 }
複製代碼

3-5 做用域和閉包 - 做用域

做用域

  • 沒有塊級做用域
  • 只有函數和全局做用域
// 無塊級做用域
if (true) {
  var name = 'negrochn'
}
console.log(name) // "negrochn"

// 只有函數和全局做用域
var a = 100
function fn() {
  var a = 200
  console.log('fn', a)
}
console.log('global', a) // "global" 100
fn() // "fn" 200
複製代碼

做用域鏈

var a = 100
function fn() {
  var b = 200
  // 當前做用域沒有定義的變量,即「自由變量」
  console.log(a)
  console.log(b)
}
fn()
複製代碼
var a = 100
function f1() {
  var b = 200
  function f2() {
    var c = 300
    console.log(a) // a 是自由變量
    console.log(b) // b 是自由變量
    console.log(c)
  }
  f2()
}
f1()
複製代碼

3-6 做用域和閉包 - 做用域 代碼演示

3-7 補充 - ES6 塊級做用域

JS 沒有塊級做用域,ES6 有塊級做用域

3-8 做用域和閉包 - 閉包

閉包

function f1() {
  var a = 100
  // 返回一個函數(函數做爲返回值)
  return function() {
    console.log(a)
  }
}
// f1 獲得一個函數
var f = f1()
var a = 200
f() // 100
複製代碼

閉包的使用場景

  • 函數做爲返回值
  • 函數做爲參數傳遞

3-9 做用域和閉包 - 閉包 代碼演示

// 閉包 1 ,函數做爲返回值
function f1() {
  var a = 100
  return function() {
    console.log(a) // a 是自由變量,向父級做用域去尋找,函數定義時的父做用域
  }
}
var f = f1()
var a = 200
f() // 100
複製代碼
// 閉包 2 ,函數做爲參數傳遞
function f1() {
  var a = 100
  return function() {
    console.log(a)
  }
}
var f = f1()
function f2(fn) {
  var a = 200
  fn()
}
f2(f) // 100
複製代碼

3-10 做用域和閉包 - 解題

說一下對變量提高的理解

  • 變量定義
  • 函數聲明(注意和函數表達式的區別)

說明 this 幾種不一樣的使用場景

  • 做爲構造函數執行
  • 做爲對象屬性執行
  • 做爲普通函數執行
  • callapplybind

建立 10 個 <a> 標籤,點擊的時候彈出對應的序號

// 這是一個錯誤的寫法
var i, a
for(i = 0; i < 10; i++) {
  a = document.createElement('a')
  a.innerHTML = i + '<br>'
  a.addEventListener('click', function(e) {
    e.preventDefault()
    alert(i) // 自由變量,要去父做用域獲取值
  })
  document.body.appendChild(a)
}
複製代碼
// 這是正確的寫法
var i
for(i = 0; i < 10; i++) {
  (function(i) {
    var a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function(e) {
      e.preventDefault()
      alert(i)
    })
    document.body.appendChild(a)
  })(i)
}
複製代碼

若是理解做用域

  • 自由變量
  • 做用域鏈,即自由變量的查找
  • 閉包的兩個場景

實際開發中閉包的應用

// 閉包實際應用中主要用於封裝變量,收斂權限
function isFirstLoad() {
  var _list = []
  return function(id) {
    if (_list.indexOf(id) >= 0) {
      return false
    } else {
      _list.push(id)
      return true
    }
  }
}

// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10)) // true
console.log(firstLoad(10)) // false
console.log(firstLoad(20)) // true
// 你在 isFirstLoad 函數外,根本不可能修改掉 _list 的值
複製代碼

3-11 做用域和閉包 - 解題 代碼演示

第4章 JS 基礎知識(下)

4-1 異步和單線程 - 什麼是異步

題目

  • 同步和異步的區別是什麼?分別舉一個同步和異步的例子
  • 一個關於 setTimeout 的筆試題
  • 前端使用異步的場景有哪些?

知識點

  • 什麼是異步(對比同步)
  • 前端使用異步的場景
  • 異步和單線程

什麼是異步

  • 同步阻塞後續代碼執行
  • 異步不會阻塞程序的運行
console.log(100)
setTimeout(function() {
  console.log(200)
}, 1000)
console.log(300)
複製代碼

對比同步

console.log(100)
alert(200) // 1 秒以後手動點擊確認
console.log(300)
複製代碼

什麼時候須要異步

  • 在可能發生等待的狀況
  • 等待過程當中不能像 alert 同樣阻塞程序運行
  • 所以,全部的「等待的狀況」都須要異步

前端使用異步的場景

  • 定時任務,setTimeoutsetInterval
  • 網絡請求,Ajax 請求、動態 <img> 加載
  • 事件綁定
// Ajax 請求
console.log('start')
$.get('./data1.json', function(data1) {
  console.log(data1)
})
console.log('end')
複製代碼
// 動態 <img> 加載
console.log('start')
var img = document.createElement('img')
img.onload = function() {
  console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
複製代碼
// 事件綁定
console.log('start')
document.getElementById('btn1').addEventListener('click', function() {
  alert('clicked')
})
console.log('end')
複製代碼

4-2 異步和單線程 - 什麼是異步 代碼演示

4-3 異步和單線程 - 單線程

console.log(100)
setTimeout(function() {
  console.log(200)
})
console.log(300)
複製代碼
  1. 執行第 1 行,打印 100
  2. 執行 setTimeout 後,傳入 setTimeout 的函數會被暫存起來,不會當即執行(單線程的特色,不能同時幹兩件事)
  3. 執行第 5 行,打印 300
  4. 待全部程序執行完,處於空閒狀態時,會立馬看有沒有暫存起來要執行的
  5. 發現暫存起來的 setTimeout 中的函數,無須等待時間,就當即過來執行

單線程

  • JS 是單線程語言,即一次只能幹一件事,若是同時有兩件事,另外一件就先上一邊排隊去,我先幹完這件事再說,若是沒有異步,那就會出現阻塞現象
  • 因爲 JS 是單線程,在代碼執行的時候又不能由於執行須要等待的代碼而形成阻塞,所以 JS 會首先將無需等待的(同步)代碼執行完成後,再來處理異步代碼,若是達到異步代碼的執行條件的話,就會執行

4-4 異步和單線程 - 解答

同步和異步的區別是什麼?分別舉一個同步和異步的例子

  • 同步會阻塞代碼執行,而異步不會
  • alert 是同步,setTimeout 是異步

一個關於 setTimeout 的筆試題

console.log(1)
setTimeout(function() {
  console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
  console.log(4)
}, 1000)
console.log(5)
// 1
// 3
// 5
// 2
// 4 ,一秒後
複製代碼

前端使用異步的場景有哪些?

  • 定時任務,setTimeoutsetInterval
  • 網絡請求,Ajax 請求、動態 <img> 加載
  • 事件綁定

重點總結

  • 異步和同步的區別
  • 異步和單線程的關係
  • 異步在前端的使用場景

4-5 其餘知識點 - Date 和 Math

題目

  • 獲取 2020-02-24 格式的日期
  • 獲取隨機數,要求長度一致的字符串格式
  • 寫一個能遍歷對象和數組的通用 forEach 函數

知識點

  • Date
  • Math
  • 數組 API
  • 對象 API

Date

Date.now() // 獲取當前時間毫秒數
var dt = new Date()
dt.getTime() // 獲取毫秒數
dt.getFullYear() // 年
dt.getMonth() // 月(0-11)
dt.getDate() // 日(1-31)
dt.getHours() // 時(0-23)
dt.getMinutes() // 分(0-59)
dt.getSeconds() // 秒(0-59)
複製代碼

Math

Math.random() // 獲取隨機數
複製代碼

4-6 其餘知識點 - 數組和對象的 API

數組 API

  • forEach ,遍歷全部元素
  • every ,判斷全部元素是否都符合條件
  • some ,判斷是否有至少一個元素符合條件
  • sort ,排序
  • map ,對元素從新組裝,生成新數組
  • filter ,過濾符合條件的元素
// forEach
var arr = [1, 2, 3]
arr.forEach(function(item, index) {
  // 遍歷數組的全部元素
  console.log(index, item)
})
// 0 1
// 1 2
// 2 3
複製代碼
// every
var arr = [1, 2, 3]
var result = arr.every(function(item, index) {
  // 用來判斷全部的數組元素,都知足條件
  if (item < 4) {
    return true
  }
})
console.log(result) // true
複製代碼
// some
var arr = [1, 2, 3]
var result = arr.some(function(item, index) {
  // 用來判斷只要有一個數組元素知足條件
  if (item < 2) {
    return true
  }
})
console.log(result) // true
複製代碼
// sort
var arr = [1, 4, 2, 3, 5]
var result = arr.sort(function(a, b) {
  // 從小到大排序
  return a - b // 從大到小排序 return b - a
})
console.log(result) // [1, 2, 3, 4, 5]
複製代碼
// map
var arr = [1, 2, 3]
var result = arr.map(function(item, index) {
  // 將元素從新組裝並返回
  return '<b>' + item + '</b>'
})
console.log(result) //  ["<b>1</b>", "<b>2</b>", "<b>3</b>"]
複製代碼
// filter
var arr = [1, 2, 3]
var result = arr.filter(function(item, index) {
  // 經過某一個條件過濾數組
  if (item >= 2) {
    return true
  }
})
console.log(result) // [2, 3]
複製代碼

對象 API

var obj = {
  x: 100,
  y: 200,
  z: 300
}
var key
for (key in obj) {
  // 注意這裏的 hasOwnProperty ,在將原型鏈的時候講過了
  if (obj.hasOwnProperty(key)) {
    console.log(key, obj[key])
  }
}
// x 100
// y 200
// z 300
複製代碼

4-7 其餘知識點 - 知識點 代碼演示

4-8 其餘知識點 - 解答

獲取 2020-02-24 格式的日期

function formatDate(dt) {
  if (!dt) {
    dt = new Date()
  }
  var year = dt.getFullYear()
  var month = dt.getMonth() + 1
  var date = dt.getDate()
  return year + '-' + month.toString().padStart(2, '0') + '-' + date.toString().padStart(2, '0')
}

formatDate(new Date()) // "2021-02-24"
複製代碼

獲取隨機數,要求長度一致的字符串格式

var random = Math.random()
random = random + '0000000000'
random = random.slice(0, 10)
console.log(random)
複製代碼

寫一個能遍歷對象和數組的通用 forEach 函數

function forEach(obj, fn) {
  var key
  if (obj instanceof Array) {
    obj.forEach(function(item, index) {
      fn(index, item)
    })
  } else {
    for (key in obj) {
      fn(key, obj[key])
    }
  }
}

var arr = [1, 2, 3]
forEach(arr, function(key, value) {
  console.log(key, value)
})
// 0 1
// 1 2
// 2 3

var obj = {
  x: 100,
  y: 200
}
forEach(obj, function(key, value) {
  console.log(key, value)
})
// x 100
// y 200
複製代碼

重點總結

  • Date
  • Math
  • 數組 API
  • 對象 API

4-9 其餘知識點 - 代碼演示

第5章 JS Web API(上)

5-1 從基礎知識到 JS Web API

回顧 JS 基礎知識

  • 變量類型和計算
  • 原型和原型鏈
  • 閉包和做用域
  • 異步和單線程
  • 其餘(如 Date 、Math 、各類經常使用 API)
  • 特色:表面看起來並不能用於工做中開發代碼
  • 內置函數:Object 、Array 、Boolean 、String ...
  • 內置對象:Math 、JSON ...
  • 咱們連在網頁彈出一句 Hello World 都不能實現

JS Web API

  • JS 基礎知識:ECMA262 標準
  • JS Web API:W3C 標準

W3C 標準中關於 JS 的規定有

  • DOM 操做
  • BOM 操做
  • 事件綁定
  • Ajax 請求(包括 http 協議)
  • 存儲

頁面彈框是 window.alert(123) ,瀏覽器須要作

  • 定義一個 window 全局變量,對象類型
  • 給他定義一個 alert 屬性,屬性值是一個函數

獲取元素 document.getElementById(id) ,瀏覽器須要

  • 定義一個 document 全局變量,對象類型
  • 給它定義一個 getElementById 的屬性,屬性值是一個函數

可是 W3C 標準沒有規定任何 JS 基礎相關的東西

  • 無論什麼變量類型、原型、做用域和異步
  • 只管定義用於瀏覽器中 JS 操做頁面的 API 和全局變量

全面考慮,JS 內置的全局函數和對象有哪些?

  • 以前講過的 Object 、Array 、Boolean 、String 、Math 、JSON 等
  • 剛剛提到的 window 、document
  • 接下來還有繼續講到的全部未定義的全局變量,如 navigator.userAgent

總結

常說的 JS(瀏覽器執行的 JS)包含兩部分

  • JS 基礎知識(ECMA262 標準)
  • JS Web API(W3C 標準)

5-2 DOM 本質

DOM ,全稱 Document Object Model

題目

  • DOM 是哪一種基本的數據結構?
  • DOM 操做的經常使用 API 有哪些?
  • DOM 節點的 Attribute 和 Property 有何區別?

知識點

  • DOM 本質
  • DOM 節點操做
  • DOM 結構操做

DOM 本質

<?xml version="1.0" encoding="utf-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend</body>
  <other>
    <a></a>
    <b></b>
  </other>
</note>
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <p>this is p</p>
  </div>
</body>
</html>
複製代碼

DOM 本質:瀏覽器拿到 HTML 代碼後,DOM 把 HTML 代碼結構化成瀏覽器可識別以及 JS 可識別的東西。

HTML 代碼就是一個字符串,可是瀏覽器已經把字符串結構化成樹形結構了。

5-3 DOM 節點操做

DOM 能夠理解爲:瀏覽器把拿到的 HTML 代碼,結構化成一個瀏覽器能識別而且 JS 可操做的一個模型而已。

DOM 節點操做

  • 獲取 DOM 節點
  • Property ,JS 對象屬性
  • Attribute ,HTML 標籤屬性

獲取 DOM 節點

var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName('div') // 集合
console.log(divList.length)
console.log(divList[0])

var containerList = document.getElementsByClassName('container') // 集合
var pList = document.querySelectorAll('p') // 集合
複製代碼

Property ,JS 對象屬性

var pList = document.querySelectorAll('p')
var 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) // "P"
console.log(p.nodeType) // 1
複製代碼

Attribute ,HTML 標籤屬性

var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size: 30px;')
複製代碼

5-4 DOM 節點操做 - 代碼演示

5-5 DOM 結構操做

DOM 結構操做

  • 新增節點
  • 獲取父元素
  • 獲取子元素
  • 刪除節點

新增節點

var div1 = document.getElementById('div1')
// 添加新節點
var p = document.createElement('p')
p.innerHTML = 'new p'
div1.appendChild(p) // 添加新建立的元素
// 移動已有節點
var p4 = document.getElementById('p4')
div1.appendChild(p4)
複製代碼

獲取父元素和子元素

var div1 = document.getElementById('div1')
var parent = div1.parentNode

var children = div1.childNodes
複製代碼

刪除節點

div1.removeChild(children[0])
複製代碼

5-6 DOM 結構操做 - 代碼演示

5-7 DOM 結構操做 - 解答

DOM 是哪一種基本的數據結構?

答:樹

DOM 操做的經常使用 API 有哪些?

答:

  • 獲取 DOM 節點,以及節點的 Property 和 Attribute
  • 獲取父節點,獲取子節點
  • 新增節點,刪除節點

DOM 節點的 Attribute 和 Property 有何區別?

答:

  • Property 只是一個 JS 對象的屬性的修改
  • Attribute 是對 HTML 標籤屬性的修改

重點總結

  • DOM 本質
  • DOM 節點操做
  • DOM 結構操做

5-8 BOM 操做

題目

  • 如何檢測瀏覽器的類型
  • 拆解 URL 的各部分

知識點

  • navigator
  • srceen
  • location
  • history

navigator & screen

// navigator
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome) // 81

// screen
console.log(screen.width) // 1920
console.log(screen.height) // 1080
複製代碼

location & history

// location ,以 http://localhost:8080/login?username=negrochn&password=123456#mid=1 爲例
console.log(location.href) // http://localhost:8080/login?username=negrochn&password=123456#mid=1
console.log(location.protocol) // http:
console.log(location.host) // localhost:8080
console.log(location.hostname) // localhost
console.log(location.port) // 8080
console.log(location.pathname) // /login
console.log(location.search) // ?username=negrochn&password=123456
console.log(location.hash) // #mid=1

// history
history.back()
history.forward()
複製代碼

5-9 BOM 操做 - 代碼演示

第6章 JS Web API(下)

6-1 事件

題目

  • 編寫一個通用的事件監聽函數
  • 描述事件冒泡流程
  • 對一個無限下拉加載圖片的頁面,如何給每一個圖片綁定事件

知識點

  • 通用事件綁定
  • 事件冒泡
  • 代理

通用事件綁定

var btn = document.getElementById('btn1')
btn.addEventListener('click', function(e) {
  console.log('clicked')
})

function bindEvent(elem, type, fn) {
  elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', function(e) {
  e.preventDefault() // 阻止默認行爲
  alert('clicked')
})
複製代碼

關於 IE 低版本的兼容性

  • IE 低版本使用 attachEvent 綁定事件,和 W3C 標準不同
  • IE 低版本使用量已很是少,不少網站都早已不支持
  • 建議對 IE 低版本的兼容性:瞭解便可,無需深究
  • 若是遇到對 IE 低版本要求苛刻的面試,果斷放棄

事件冒泡

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>事件冒泡</title>
</head>
<body>
  <div id="div1">
    <p id="p1">激活</p>
    <p id="p2">取消</p>
    <p id="p3">取消</p>
    <p id="p4">取消</p>
  </div>
  <div id="div2">
    <p id="p5">取消</p>
    <p id="p6">取消</p>
  </div>
  <script> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var p1 = document.getElementById('p1') var body = document.body bindEvent(p1, 'click', function(e) { e.stopPropagation() alert('激活') }) bindEvent(body, 'click', function(e) { alert('取消') }) </script>
</body>
</html>
複製代碼

代理

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>事件冒泡</title>
</head>
<body>
  <div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
    <a href="#">a4</a>
    <!-- 會隨時新增更多 a 標籤 -->
  </div>
  <script> var div1 = document.getElementById('div1') div1.addEventListener('click', function(e) { var target = e.target if (target.nodeName === 'A') { alert(target.innerHTML) } }) </script>
</body>
</html>
複製代碼

完善通用綁定事件的函數

function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector
    selector = null
  }
  elem.addEventListener(type, function(e) {
    var target
    if (selector) {
      target = e.target
      if (target.matches(selector)) {
        fn.call(target, e)
      }
    } else {
      fn(e)
    }
  })
}

// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function(e) {
  console.log(this.innerHTML)
})

// 不使用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function(e) {
  console.log(a.innerHTML)
})
複製代碼

代理的好處

  • 代碼簡潔
  • 減小瀏覽器內存佔用

6-2 事件 - 代碼演示

6-3 事件 - 解答

編寫一個通用的事件監聽函數

答:

function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector
    selector = null
  }
  elem.addEventListener(type, function(e) {
    let target
    if (selector) {
      target = e.target
      if (target.matches(selector)) {
        fn.call(target, e)
      }
    } else {
      fn(e)
    }
  })
}
複製代碼

描述事件冒泡流程

答:

  1. DOM 樹形結構
  2. 事件冒泡
  3. 阻止冒泡
  4. 冒泡的應用

對一個無限下拉加載圖片的頁面,如何給每一個圖片綁定事件

答:

  • 使用代理
  • 代理的兩個優勢(代碼簡潔、減小瀏覽器內存佔用)

重點總結

  • 通用事件綁定
  • 事件冒泡
  • 事件代理

6-4 Ajax - XMLHttpRequest

題目

  • 手寫一個 Ajax ,不依賴第三方庫
  • 跨域的幾種實現方式

知識點

  • XMLHttpRequest
  • 狀態碼說明
  • 跨域

XMLHttpRequest

var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
  // 這裏的函數異步執行
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      alert(xhr.responseText)
    }
  }
}
xhr.send(null)
複製代碼

IE 兼容性問題

  • IE 低版本使用 ActiveXObject ,和 W3C 標準不同
  • IE 低版本使用量已很是少,不少網站都早已不支持
  • 建議對 IE 低版本的兼容性:瞭解便可,無需深究
  • 若是遇到對 IE 低版本要求苛刻的面試,果斷放棄

readyState

  • 0 - 未初始化,尚未調用 send() 方法
  • 1 - 載入,已調用 send() 方法,正在發送請求
  • 2 - 載入完成,send() 方法執行完成,已經接收到所有響應內容
  • 3 - 交互,正在解析響應內容
  • 4 - 完成,響應內容解析完成,能夠在客戶端調用了

status

  • 1XX - 信息,服務器收到請求,須要請求者繼續執行操做
  • 2XX - 成功,操做被成功接收並處理
  • 3XX - 重定向,須要進一步的操做以完成請求
  • 4XX - 客戶端錯誤,請求包含語法錯誤或沒法完成請求
  • 5XX - 服務器錯誤,服務器在處理請求的過程當中發生了錯誤
狀態碼 英文描述 中文描述
100 Continue 繼續。客戶端應繼續其請求。
200 OK 請求成功。
204 No Content 無內容。服務器成功處理,但未返回內容。
206 Partial Content 部份內容。服務器成功處理了部分 GET 請求。
301 Moved Permanently 永久移動。請求的資源已被永久的移動到新 URI ,返回信息會包括新的 URI ,瀏覽器會自動定向到新 URI 。
302 Found 臨時移動。客戶端應繼續使用原有 URI 。
304 Not Modified 未修改。所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。
307 Temporary Redirect 臨時重定向。使用 GET 請求重定向。
400 Bad Request 客戶端請求的語法錯誤,服務器沒法理解。
401 Unauthorized 請求要求用戶的身份認證。
403 Forbidden 服務器理解請求客戶端的請求,可是拒絕執行此請求。
404 Not Found 服務器沒法根據客戶端的請求找到資源(網頁)。
500 Internal Server Error 服務器內部錯誤,沒法完成請求。
502 Bad Gateway 做爲網關或代理工做的服務器嘗試執行請求時,從遠程服務器接收到了一個無效的響應。
503 Service Unavailable 因爲超載或系統維護,服務器暫時的沒法處理客戶端的請求。

6-5 Ajax - 跨域

什麼是跨域

  • 瀏覽器有同源策略,不容許 Ajax 訪問其餘域接口
  • 跨域條件:協議、域名、端口,有一個不一樣就算跨域
  • 有三個標籤容許跨域加載資源
    • <img src=xxx> ,用於打點統計,統計網站多是其餘域
    • <link href=xxx> ,可使用 CDN
    • <script src=xxx> ,可使用 CDN ,用於 JSONP

跨域注意事項

  • 全部的跨域請求都必須通過信息提供方容許
  • 若是未經容許便可獲取,那是瀏覽器同源策略出現漏洞

JSONP 實現原理

  • 加載 coding.imooc.com/lesson/115.…
  • 不必定服務器端真正有一個 115.html 文件
  • 服務器能夠根據請求,動態生成一個文件返回
  • 同理,<script src="http://coding.imooc.com/api.js">
  • 例如你的網站要跨域訪問慕課網的一個接口
  • 慕課給你一個地址 coding.imooc.com/api.js
  • 返回內容格式如 callback({ x: 100, y: 200 })(可動態生成)
<script> window.callback = function(data) { // 這是咱們跨域獲得的信息 console.log(data) } </script>
<script src="http://coding.imooc.com/api.js"></script>
<!-- 以上將返回 callback({ x: 100, y: 200 }) -->
複製代碼

服務器設置 http header

  • 另一個解決跨域的簡潔方法,須要服務器來作
  • 可是做爲交互方,咱們必須知道這個方法
  • 是未來解決跨域問題的一個趨勢
// 注意:不一樣後端語言的寫法可能不同

// 第二個參數填寫容許跨域的域名稱,不建議直接寫 *
response.setHeader('Access-Control-Allow-Origin', 'http://a.com, http://b.com')
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With')
response.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')

// 接收跨域的 Cookie
response.setHeader('Access-Control-Allow-Credentials', 'true')
複製代碼

手寫一個 Ajax ,不依賴第三方庫

var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      alert(xhr.responseText)
    }
  }
}
xhr.send(null)
複製代碼

跨域的幾種實現方式

  • JSONP
  • 服務器端設置 http header

重點總結

  • XMLHttpRequest
  • 狀態碼
  • 跨域

6-6 存儲

題目

  • 請描述一下 Cookie 、sessionStorage 和 localStorage 的區別?

知識點

  • Cookie
  • sessionStorage 和 localStorage

Cookie

  • 自己用於客戶端和服務器端通訊
  • 可是它有本地存儲的功能,因而就被「借用」
  • 使用 document.cookie 獲取和修改

Cookie 用於存儲的缺點

  • 存儲量過小,只有 4KB
  • 全部 http 請求都帶着,會影響獲取資源的效率
  • API 簡單,須要封裝才能用 document.cookie

localStorage 和 sessionStorage

  • HTML5 專門爲存儲而設計,最大容量 5MB
  • API 簡單易用,getItem(key)setItem(key, value)
  • iOS Safari 隱藏模式下,localStorage.getItem 會報錯,建議統一使用 try-catch 封裝

請描述一下 Cookie 、sessionStorage 和 localStorage 的區別?

  • 容量
  • 是否攜帶到請求中
  • API 易用性

第7章 開發環境

7-1 介紹

關於開發環境

  • 面試官想經過開發環境瞭解面試者的經驗
  • 開發環境,最能體現工做產出的效率
  • 會以聊天的形式爲主,而不是出具體的問題

開發環境包含

  • IDE(寫代碼的效率)
  • Git(代碼版本管理,多人協做開發)
  • JS 模塊化
  • 打包工具
  • 上線回滾的流程

7-2 IDE

IDE

  • WebStorm
  • Sublime
  • VS Code(推薦)
  • Atom

7-3 Git - 經常使用命令

Git

  • 正式項目都須要代碼版本管理
  • 大型項目須要多人協做開發
  • Git 和 Linux 是一個做者
  • 網絡 Git 服務器,如 github
  • 通常公司代碼非開源,都有本身的 Git 服務器
  • 搭建 Git 服務器無需你瞭解太多
  • Git 的基本操做必須很熟練

經常使用 Git 命令

  • git add .
  • git checkout xxx ,還原文件
  • git commit -m 'xxx' ,提交到本地倉庫
  • git push origin master ,提交到遠程倉庫
  • git pull origin master ,下載遠程倉庫的文件
  • git branch ,建立分支
  • git checkout -b xxx/git checkout xxx ,新建一個分支/切換到另外一個分支
  • git merge xxx ,把以前的分支拷貝到這裏

7-4 Git - 代碼演示

7-5 Git - 代碼演示 多人協做

多人開發

  • 爲了編寫屬於本身的功能,能夠建立本身的分支,而不要在主分支上改動,最後進行合併便可

步驟

  1. git checkout -b dev ,建立一個 dev 分支
  2. vi a.js修改內容(vi 屬於 Linux 命令,git status 查看是否被修改,git diff 查看修改的內容)
  3. git add .
  4. git commit -m 'update dev'
  5. git push origin dev ,提交到遠程倉庫的 dev 分支
  6. 半個月以後,合併到 master 分支
  7. git checkout master ,切換到 master 分支
  8. git pull origin master ,下載遠程倉庫的 master 分支的文件
  9. git merge dev ,把 dev 分支的修改內容拷貝到 master 分支
  10. git push origin master ,提交到遠程倉庫的 master 分支

附加命令

  • cat 文件名,查看文件內容
  • git diff ,查看修改的內容
  • git branch ,查看分支和當前在哪條分支上
  • git status ,查看文件是否被修改的狀態
  • vi 文件 ,新建文件並打開文件
  • esc + :wq ,退出並保存文件

7-6 模塊化 - AMD

不使用模塊化

// util.js
function getFormatDate(date, type) {
  // type === 1 返回 2021-03-06
  // type === 2 返回 2021年3月6日
  // ...
}
複製代碼
// a-util.js
function aGetFormatDate(date) {
  // 要求返回 2021年3月6日格式
  return getFormatDate(date, 2)
}
複製代碼
// a.js
var dt = new Date()
console.log(aGetFormatDate(dt))
複製代碼
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<!-- 1. 這些代碼中的函數必須是全局變量,才能暴露給使用方。全局變量污染。 -->
<!-- 2. a.js 知道要引用 a-util.js ,可是他知道還須要依賴於 util.js 嗎 -->
複製代碼

AMD

  • require.js
  • 全局 define 函數
  • 全局 require 函數
  • 依賴 JS 會自動、異步加載

使用 require.js

// util.js
define(function() {
  return {
    getFormatDate: function(date, type) {
      if (type === 1) {
        return '2021-03-06'
      } else if (type === 2) {
        return '2021年3月6日'
      }
    }
  }
})
複製代碼
// a-util.js
define(['./util.js'], function(util) {
  return {
    aGetFormatDate: function(date) {
      return util.getFormatDate(date, 2)
    }
  }
})
複製代碼
// a.js
define(['./a-util.js'], function(aUtil) {
  return {
    printDate: function(date) {
      console.log(aUtil.aGetFormatDate(date))
    }
  }
})
複製代碼
// main.js
require(['./a.js'], function(a) {
  var date = new Date()
  a.printDate(date)
})
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AMD</title>
</head>
<body>
  <script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.min.js" data-main="./main.js"></script>
</body>
</html>
複製代碼

7-7 模塊化 - AMD 代碼演示

7-8 模塊化 - CommonJS

CommonJS

  • Node.js 模塊化規範,如今被前端大量使用,緣由:
  • 前端開發依賴的插件和庫,均可以從 npm 中獲取
  • 構建工具的高度自動化,使得使用 npm 的成本很是低
  • CommonJS 不會異步加載 JS ,而是同步一次性加載出來

使用 CommonJS

// util.js
module.exports = {
  getFormatDate: function(date, type) {
    if (type === 1) {
      return '2021-03-06'
    } else if (type === 2) {
      return '2021年3月6日'
    }
  }
}
複製代碼
// a-util.js
var util = require('util.js')
module.exports = {
  aGetFormatDate: function(date) {
    return util.getFormatDate(date, 2)
  }
}
複製代碼

AMD 和 CommonJS 的使用場景

  • 須要異步加載 JS ,使用 AMD
  • 使用了 npm 以後建議使用 CommonJS

重點總結

  • AMD
  • CommonJS
  • 二者的區別

7-9 構建工具 - 安裝 Node.js

7-10 構建工具 - 安裝 Webpack

7-11 構建工具 - 配置 Webpack

7-12 構建工具 - 使用 jQuery

7-13 構建工具 - 壓縮 JS

7-14 上線回滾 - 上線回滾流程

7-15 上線回滾 - Linux 基礎命令

8章 運行環境

8-1 介紹

運行環境

  • 瀏覽器就能夠經過訪問連接來獲得頁面的內容
  • 經過繪製和渲染,顯示出頁面的最終的樣子
  • 整個過程當中,咱們須要考慮什麼問題?

知識點

  • 頁面加載過程
  • 性能優化
  • 安全性

8-2 頁面加載 - 渲染過程

題目

  • 從輸入 URL 到獲得 HTML 的詳細過程
  • window.onloadDOMContentLoaded 的區別

知識點

  • 加載資源的形式
  • 加載一個資源的過程
  • 瀏覽器渲染頁面的過程

加載資源的形式

  • 輸入 URL(或跳轉頁面)加載 HTML
  • 加載 HTML 中的靜態資源

加載一個資源的過程

  • 瀏覽器根據 DNS 服務器獲得域名的 IP 地址
  • 向這個 IP 的機器發送 HTTP 請求
  • 服務器收到、處理並返回 HTTP 請求
  • 瀏覽器獲得返回內容

瀏覽器渲染頁面的過程

  • 根據 HTML 結構生成 DOM Tree
  • 根據 CSS 生成 CSSOM
  • 將 DOM 和 CSSOM 整合造成 Render Tree
  • 根據 Render Tree 開始渲染和展現
  • 遇到 <script> 時,會執行並阻塞渲染

8-3 頁面加載 - 幾個示例

爲什麼要把 CSS 放在 head 中?

答:保證先加載 CSS ,接着渲染,否則要渲染兩次,用戶體驗差。

爲什麼要把 JS 放在 body 最下面?

答:不會阻塞渲染過程,提升性能。

window.onloadDOMContentLoaded

window.addEventListener('load', function() {
  // 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
  // DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼

8-4 頁面加載 - 解答

從輸入 URL 到獲得 HTML 的詳細過程

  • 瀏覽器根據 DNS 服務器獲得域名的 IP 地址
  • 向這個 IP 的機器發送 HTTP 請求
  • 服務器收到、處理並返回 HTTP 請求
  • 瀏覽器獲得返回內容

window.onloadDOMContentLoaded 的區別

  • 頁面的所有資源加載完纔會執行,包括圖片、視頻等
  • DOM 渲染完便可執行,此時圖片、視頻尚未加載完

8-5 性能優化 - 優化策略

性能優化

  • 這自己就是一個綜合性的問題
  • 沒有標準答案,若是要很是全面,能寫一本書
  • 只關注核心店,針對面試

原則

  • 多使用內存、緩存或者其餘方法
  • 減小 CPU 計算、減小網絡

從哪裏入手

  • 加載頁面和靜態資源
  • 頁面渲染

加載資源優化

  • 靜態資源的壓縮合並
  • 靜態資源緩存
  • 使用 CDN 讓資源加載更快
  • 使用 SSR 後端渲染,數據直接輸出到 HTML 中

渲染優化

  • CSS 放前面,JS 放後面
  • 懶加載(圖片懶加載、下拉加載更多)
  • 減小 DOM 操做,對 DOM 查詢作緩存,多個操做盡可能合併在一塊兒執行
  • 事件節流
  • 儘早執行操做,如 DOMContentLoaded

8-6 性能優化 - 幾個示例

資源合併

<script src=a.js></script>
<script src=b.js></script>
<script src=c.js></script>
複製代碼
<script src="abc.js"></script>
複製代碼

緩存

  • 經過連接名稱控制緩存
  • 只有內容改變的時候,連接名稱纔會改變

CDN

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
複製代碼

使用 SSR 後端渲染

  • 如今 Vue/React 提出了這樣的概念
  • 其實 JSP/PHP/ASP 都屬於後端渲染

懶加載

<img id="img1" src="preview.png" data-realsrc="abc.png" />
<script> var img1 = document.getElementById('img1') img1.src = img1.getAttribute('data-realsrc') </script>
複製代碼

緩存 DOM 查詢

// 未緩存 DOM 查詢
var i
for (i = 0; i < document.getElementByTagName('p').length; i++) {
  
}

// 緩存了 DOM 查詢
var pList = document.getElementByTagName('p')
var i
for (i = 0; i < pList.length; i++) {
  
}
複製代碼

合併 DOM 插入

var listNode = document.getElementById('list')

// 要插入 10 個 li 標籤
var frag = document.createDocumentFragment()
var x, li
for (x = 0; x < 10; x++) {
  li = document.createElement('li')
  li.innerHTML = 'List item ' + x
  frag.appendChild(li)
}
listNode.appendChild(frag)
複製代碼

事件節流

var textarea = document.getElementById('text')
var timeoutid
textarea.addEventListener('keyup', function() {
  if (timeoutid) {
    clearTimeout(timeoutid)
  }
  timeoutid = setTimeout(function() {
    // 觸發 change 事件
  }, 100)
})
複製代碼

儘早操做

window.addEventListener('load', function() {
  // 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
  // DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼

8-7 安全性 - XSS

知識點

  • XSS 跨站請求攻擊
  • XSRF 跨站請求僞造

XSS

  • 在新浪博客寫一篇文章,同時偷偷插入一段 <script>
  • 攻擊代碼中,獲取 Cookie ,發送本身的服務器
  • 發佈博客,有人查看博客內容
  • 會把查看者的 Cookie 發送到攻擊者的服務器
  • 前端替換關鍵字,例如替換 <&lt;>&gt;
  • 後端替換

8-8 安全性 - XSRF

XSRF

  • 你已登陸一個購物網站,正在瀏覽商品
  • 該網站付費接口是 xxx.com/pay?id=100 ,可是沒有任何驗證
  • 而後你收到一封郵件,隱藏着 <img src="xxx.com/pay?id=100" />
  • 你查看郵件的時候,就已經悄悄的付費購買了
  • 增長驗證流程,如輸入指紋、密碼、短信驗證碼

8-9 面試技巧

簡歷

  • 簡潔明瞭,重點突出項目經理和解決方案
  • 把我的博客放在簡歷中,而且按期維護更新博客
  • 把我的的開源項目放在簡歷中,並維護開源項目
  • 簡歷千萬不要造假,要保持能力和經歷上的真實性

面試過程當中

  • 如何看待加班?加班就像借錢,救急不救窮
  • 千萬不要挑戰面試官,不要反考面試官
  • 學會給面試官驚喜,但不要太多
  • 遇到不會回答的問題,說出你知道的也能夠
  • 談談你的缺點——說一下你最近正在學什麼就能夠了

第9章 真題模擬

  1. varletconst 的區別

    答:

    • var 是 ES5 語法,letconst 是ES6 語法,var 有變量提高
    • varlet 是變量,可修改,const 是常量,不可修改
    • letconst 有塊級做用域,var 沒有
  2. typeof 返回哪些類型

    答:

    • undefined 、string 、number 、boolean 、symbol
    • object(注意,typeof null === 'object'
    • function
  3. 列舉強制類型轉換和隱式類型轉換

    答:

    • 強制:parseIntparseFloattoString
    • 隱式:if 、邏輯運算、== 、+ 拼接字符串
  4. 手寫深度比較,模擬 lodash 的 isEqual

    答:

    // 判斷是不是對象或數組
    function isObject(obj) {
      return typeof obj === 'object' && obj !== null
    }
    function isEqual(obj1, obj2) {
      if (!isObject(obj1) || !isObject(obj2)) {
        // 值類型(注意,參與 equal 的通常不會是函數)
        return obj1 === obj2
      }
      if (obj1 === obj2) {
        return true
      }
      // 兩個都是對象或數組,並且不相等
      // 1. 先取出 obj1 和 obj2 的 keys ,比較個數
      const obj1Keys = Object.keys(obj1)
      const obj2Keys = Object.keys(obj2)
      if (obj1Keys.length !== obj2Keys.length) {
        return false
      }
      // 2. 以 obj1 爲基準,和 obj2 一次遞歸比較
      for (let key in obj1) {
        if (!isEqual(obj1[key], obj2[key])) {
          return false
        }
      }
      // 3. 全相等
      return true
    }
    
    const obj1 = {
      a: 100,
      b: {
        x: 100,
        y: 200
      }
    }
    const obj2 = {
      a: 100,
      b: {
        x: 100,
        y: 200
      }
    }
    console.log(obj1 === obj2) // false
    console.log(isEqual(obj1, obj2)) // true
    複製代碼
  5. splitjoin 的區別

    答:

    '1-2-3'.split('-') // [1, 2, 3]
    [1, 2, 3].join('-') /// '1-2-3'
    複製代碼
  6. 數組的 poppushunshiftshift 分別作什麼

    答:

    • 功能是什麼?
    • 返回值是什麼?
    • 是否會對原數組形成影響?
    const arr = [10, 20, 30, 40]
    const result = arr.pop()
    console.log(result, arr) // 40, [10, 20, 30]
    複製代碼
    const arr = [10, 20, 30, 40]
    const result = arr.push(50) // 返回 length
    console.log(result, arr) // 5, [10, 20, 30, 40, 50]
    複製代碼
    const arr = [10, 20, 30, 40]
    const result = arr.unshift(5) // 返回 length
    console.log(result, arr) // 5, [5, 10, 20, 30, 40]
    複製代碼
    const arr = [10, 20, 30, 40]
    const result = arr.shift()
    console.log(result, arr) // 10, [20, 30, 40]
    複製代碼

    純函數:1. 不改變源數組;2. 返回一個數組

    // 純函數
    const arr = [10, 20, 30, 40]
    
    const cArr = arr.concat([50, 60, 70]) // [10, 20, 30, 40, 50, 60, 70]
    const mArr = arr.map(item => item * 10) // [100, 200, 300, 400]
    const fArr = arr.filter(item => item > 20) // [30, 40]
    const sArr = arr.slice() // [10, 20, 30, 40]
    
    
    // 非純函數
    // push 、pop 、shift 、unshift
    // forEach
    // some every reduce
    複製代碼
  7. 數組 slicesplice 的區別

    答:

    • 功能區別,slice 是切片,splice 是剪接
    • 參數和返回值
    • 是否純函數?
    const arr = [10, 20, 30, 40, 50]
    
    // slice 是純函數
    const sliceArr = arr.slice(1, 4)
    
    // splice 不是純函數
    const spliceArr = arr.splice(1, 2, 'a', 'b', 'c')
    console.log(arr, spliceArr) // [10, "a", "b", "c", 40, 50],[20, 30]
    複製代碼
  8. [10, 20, 30].map(parseInt) 返回結果是什麼?

    答:

    • map 的參數和返回值
    • parseInt 的參數和返回值
    [10, 20, 30].map(parseInt)
    // 至關於
    [10, 20, 30].map((item, index) => {
      return parseInt(item, index)
    })
    // 分解爲
    parseInt(10, 0) // 10
    parseInt(20, 1) // NaN
    parseInt(30, 2) // NaN
    複製代碼
  9. Ajax 請求 get 和 post 的區別?

    答:

    • get 通常用於查詢操做,post 通常用於提交操做
    • get 參數拼接在 url 上,post 放在請求體內(數據體積可更大)
    • 安全性,post 易於防止 CSRF
  10. 函數 call 和 apply 的區別?

    答:

    fn.call(this, p1, p2, p3)
    fn.apply(this, arguments)
    複製代碼
  11. 事件代理(委託)是什麼?

    答:

    function bindEvent(elem, type, selector, fn) {
      if (fn == null) {
        fn = selector
        selector = null
      }
      elem.addEventListener(type, function(e) {
        var target
        if (selector) {
          target = e.target
          if (target.matches(selector)) {
            fn.call(target, e)
          }
        } else {
          fn(e)
        }
      })
    }
    
    // 使用代理
    var div1 = document.getElementById('div1')
    bindEvent(div1, 'click', 'a', function(e) {
      console.log(this.innerHTML)
    })
    
    // 不使用代理
    var a = document.getElementById('a1')
    bindEvent(div1, 'click', function(e) {
      console.log(a.innerHTML)
    })
    複製代碼
  12. 閉包是什麼,有什麼特性?有什麼負面影響?

    答:

    • 回顧做用域和自由變量
    • 回顧閉包應用場景:做爲參數被傳入,做爲返回值被返回
    • 回顧:自由變量的查找,要在函數定義的地方(而非執行的地方)
    • 影響:變量會常駐內存,得不到釋放。閉包不要亂用
  13. 如何阻止事件冒泡和默認行爲?

    答:

    • event.stopPropagation()
    • event.preventDefault()
  14. 查找、添加、刪除、移動 DOM 節點的方法?

    答:

    • getElementByIdgetElementsByTagNamegetElementsByClassNamequerySelectorAll
    • appendChild(添加和移動)
    • removeChild
    • parentNodechildNodes
  15. 如何減小 DOM 操做?

    答:

    • 緩存 DOM 查詢結果
    • 屢次 DOM 操做,合併到一次插入
  16. 解釋 JSONP 的原理,爲什麼它不是真正的 Ajax ?

    答:

    • 瀏覽器的同源策略(服務端沒有同源策略)和跨域
    • 哪些 HTML 標籤能繞過跨域?
    • JSONP 的原理
  17. document 的 load 和 ready 的區別

    答:

    window.addEventListener('load', function() {
      // 頁面的所有資源加載完纔會執行,包括圖片、視頻等
    })
    document.addEventListener('DOMContentLoaded', function() {
      // DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
    })
    複製代碼
  18. ===== 的不一樣

    答:

    • == 會嘗試類型轉換
    • === 嚴格相等
    • 哪些場景纔會用 ==
  19. 函數聲明和函數表達式的區別

    答:

    • 函數聲明 function fn() {}
    • 函數表達式 const fn = function() {}
    • 函數聲明會在代碼執行前預加載,而函數表達式不會
  20. new Object()Object.create() 的區別

    答:

    • {} 等同於 new Object() ,原型是 Object.prototype
    • Object.create(null) 沒有原型
    • Object.crate({...}) 可指定原型
    const obj1 = {
      a: 10,
      b: 20,
      sum() {
        return this.a + this.b
      }
    }
    const obj2 = new Object({
      a: 10,
      b: 20,
      sum() {
        return this.a + this.b
      }
    })
    const obj3 = new Object(obj1)
    console.log(obj1 === obj2) // false
    console.log(obj1 === obj3) // true
    
    const obj4 = Object.create(null) // {} ,但沒有原型
    const obj5 = new Object() // {}
    const obj6 = Object.create(obj1) // 建立一個空對象,把空對象的原型指向 obj1
    console.log(obj1 === obj6) // false
    console.log(obj1 === obj6.__proto__) // true
    複製代碼
  21. 關於 this 的場景題

    const User = {
      count: 1,
      getCount: function() {
        return this.count
      }
    }
    console.log(User.getCount()) // 1
    const func = User.getCount
    console.log(func()) // undefined
    複製代碼
  22. 關於做用域和自由變量的場景題(1)

    let i
    for (i = 1; i <= 3; i++) {
      setTimeout(function() {
        console.log(i)
      }, 0)
    }
    // 4
    // 4
    // 4
    複製代碼
  23. 判斷字符串以字母開頭,後面字母數字下劃線,長度 6-30

    答:

    const reg = /^[a-zA-Z]\w{5,29}$/
    複製代碼
  24. 關於做用域和自由變量的場景題(2)

    let a = 100
    function test() {
      alert(a) // 100
      a = 10
      alert(a) // 10
    }
    test()
    alert(a) // 10
    複製代碼
  25. 手寫字符串 trim 方法,保證瀏覽器兼容性

    答:

    String.prototype.trim = function() {
      return this.replace(/^\s+/, '').replace(/\s+$/, '')
    }
    // (原型、this 、正則表達式)
    複製代碼
  26. 如何獲取多個數字中的最大值

    答:

    function max() {
      const nums = Array.prototype.slice.call(arguments) // 變爲數組
      let max = -Infinity
      nums.forEach(n => {
        if (n > max) {
          max = n
        }
      })
      return max
    }
    // 或者使用 Math.max()
    複製代碼
  27. 如何用 JS 實現繼承?

    答:

    • class 繼承
    • prototype 繼承
  28. 如何捕獲 JS 程序中的異常?

    答:

    // 第一種方式
    try {
      // TODO
    } catch(error) {
      console.error(error) // 手動捕獲
    } finally {
      // TODO
    }
    
    // 第二種方式
    // 自動捕獲
    window.onerror = function(message, source, lineNum, colNum, error) {
      // 第一,對跨域的 JS ,如 CDN 的不會有詳細的報錯信息
      // 第二,對於壓縮的 JS ,還要配合 SourceMap 反查到未壓縮代碼的行、列
    }
    複製代碼
  29. 什麼是 JSON ?

    答:

    • JSON 是一種數據格式,本質是一段字符串
    • JSON 格式和 JS 對象結構一致,對 JS 語言更友好
    • window.JSON 是一個全局對象,JSON.stringifyJSON.parse
  30. 獲取當前頁面 URL 參數

    答:

    • 傳統方式,查找 location.search
    • 新 API ,URLSearchParams
    // 傳統方式
    function query(name) {
      const search = location.search.substr(1)
      const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
      const res = search.match(reg)
      if (res === null) {
        return null
      }
      return res[2]
    }
    複製代碼
    // URLSearchParams
    function query(name) {
      const search = location.search
      const p = new URLSearchParams(search)
      return p.get(name)
    }
    複製代碼
  31. 將 URL 參數解析爲 JS 對象

    答:

    // 傳統方式,分析 search
    function query2Obj() {
      const res = {}
      const search = location.search.substr(1) // 去掉前面的 ?
      search.split('&').forEach(paramStr => {
        const arr = paramStr.split('=')
        const [key, val] = arr
        res[key] = val
      })
      return res
    }
    複製代碼
    // 使用 URLSearchParams
    function query2Obj() {
      const res = {}
      const pList = new URLSearchParams(location.search)
      pList.forEach((val, key) => {
        res[key] = val
      })
      return res
    }
    複製代碼
  32. 手寫數組 faltern ,考慮多層級

    答:

    function flat(arr) {
      // 驗證 arr 中,還有沒有深層數組
      const isDeep = arr.some(item => item instanceof Array)
      if (!isDeep) {
        return arr
      }
      return flat(Array.prototype.concat.apply([], arr))
    }
    
    const res = flat([1, 2, [3, 4], [5, [6, 7]]])
    console.log(res) // [1, 2, 3, 4, 5, 6, 7]
    複製代碼
  33. 數組去重

    答:

    • 傳統方式,遍歷元素挨個比較、去重
    • 使用 Set
    • 考慮計算效率
    // 傳統方式
    function unique(arr) {
      const res = []
      arr.forEach(item => {
        if (res.indexOf(item) < 0) {
          res.push(item)
        }
      })
      return res
    }
    console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
    複製代碼
    // 使用 Set(無序、不重複)
    function unique(arr) {
      const set = new Set(arr)
      return [...set]
    }
    console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
    複製代碼
  34. 手寫深拷貝

    答:

    function deepClone(obj = {}) {
      // 若是不是數組或對象,直接返回
      if (typeof obj !== 'object' || obj == null) {
        return obj
      }
      // 初始化返回結果
      let result
      if (obj instanceof Array) {
        result = []
      } else {
        result = {}
      }
      // 遍歷數組或對象的屬性
      for (let key in obj) {
        // 保證 key 不是原型的屬性
        if (obj.hasOwnProperty(key)) {
          // 遞歸調用
          result[key] = deepClone(obj[key])
        }
      }
      // 返回結果
      return result
    }
    
    // 注意 Object.assign 不是深拷貝
    複製代碼
  35. 介紹一下 RAF(requestAnimationFrame

    答:

    • 要想動畫流暢,更新頻率要 60 幀/秒,即 16.67ms 更新一次視圖
    • setTimeout 要手動更新頻率,而 RAF 瀏覽器會自動控制
    • 後臺標籤或隱藏 iframe 中,RAF 會暫停,而 setTimeout 依然執行
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>setTimeout</title>
      <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
      <style> #div1, #div2 { width: 100px; height: 50px; margin-bottom: 20px; background-color: red; } </style>
    </head>
    <body>
      <div id="div1">setTimeout</div>
      <div id="div2">requestAnimateFrame</div>
      <script> const $div1 = $('#div1') let curWidth = 100 const maxWidth = 640 function animate() { curWidth += 3 $div1.css('width', curWidth) if (curWidth < maxWidth) { setTimeout(animate, 16.7) } } animate() const $div2 = $('#div2') let curWidth2 = 100 function animate2() { curWidth2 += 3 $div2.css('width', curWidth2) if (curWidth2 < maxWidth) { window.requestAnimationFrame(animate2) } } animate2() </script>
    </body>
    </html>
    複製代碼
  36. 前端性能如何優化?通常從哪幾個方面考慮?

    答:

    • 原則:多使用內存、緩存,減小計算、減小網絡請求
    • 方向:加載頁面,頁面渲染,頁面操做流暢度
相關文章
相關標籤/搜索