> 下面的三級標題,代表的是此小節視頻對應的標題。四級標題為對應的面試題。
### 何為變量提升
#### 變量提升
```javascript
// 變量提升 ES5
console.log(a) // undefined
var a = 200
// 等價于下面,js 執行中會把所有 var 提升的前面;
var a
console.log(a) // undefined
a = 200
```
#### typeof 判斷類型
* undefined、string、number、boolean、symbol
* object(typeof null === 'object')
* function
### 手寫深度比較 isEqual
#### 手寫深度比較 lodash.isEqual
```javascript
// 實現效果如下
const obj1 = {a:10,b:{x:100,y:200}}
const obj2 = {a:10,b:{x:100,y:200}}
isEqual(obj1,obj2) === true
```
```javascript
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
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) {
// 比較當前 key 的 val --遞歸調用,遍歷深層次
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3、全相等
return true
}
```
#### split() 和 join()
```javascript
'1-2-3'.split('-') // [1,2,3]
[1,2,3].join('-') // '1-2-3'
```
#### 數組的 pop push unshift shift
* 功能是什么?
* 返回值是什么?
* 是否會對原數組造成影響?
```javascript
const arr = [10,20,30,40]
// pop, 刪除數組的最后一個元素,返回數組的最后一個元素值,原數組發生改變
const popRes = arr.pop()
console.log(popRes,arr) // 40,[10,20,30]
// shift,刪除數組的第一個元素,返回被刪除的元素值,原數組發生改變
const shiftRes = arr.shift()
console.log(shiftRes, arr) // 10 [ 20, 30, 40 ]
// push,向數組后面追加一個元素,返回新數組的length,原數組發生改變
const pushRes = arr.push()
console.log(pushRes, arr) // 4 [ 10, 20, 30, 40 ]
// unshift() ,在數組第一個元素前增加一個元素(傳入的參數值),返回新數組的length,原數組發生改變
const unshiftRes = arr.unshift(20)
console.log(unshiftRes, arr) // 5 [ 20, 10, 20, 30, 40 ]
```
##### 純函數
* 不改變源數組(沒有副作用)
* 返回一個數組
```javascript
// arr1.concat(arr2) 拼接兩個數組,不影響源數組,返回新數組
const arr1 = arr.concat([50, 60, 70]) // arr1:[10, 20, 30, 40, 50, 60, 70 ]
// 遍歷數組
const arr2 = arr.map((num) => num * 10) // [ 100, 200, 300, 400 ]
// 過濾數組
const arr3 = arr.filter((num) => num > 25)
// slice 從已有的數組中返回選定的元素。可以用來做深拷貝
// https://www.w3school.com.cn/jsref/jsref_slice_array.asp
const arr4 = arr.slice()
```
### 你是否真的會用數組
#### 數組 slice 和 splice 的區別
* 功能區別(slice-切片,splice-剪接)
```javascript
const arr = [10,20,30,40]
// slice(start?:number,end?:number),截取函數的一部分,返回新函數,不改變舊函數
const arr4 = arr.slice(1,3) // [ 10, 20, 30, 40 ]
// splice(start:number,deleteCount?:number),剪接函數,改變源函數
const arr5 = arr.splice(1, 2) // arr5:[ 20, 30 ] arr:[ 10, 40 ]
const spliceReg = arr.splice(1, 2, 'a', 'b', 'c') // spliceReg:[ 20, 30 ] arr:[ 10, 'a', 'b', 'c', 40 ]
```
#### [10,20,30].map(parseInt)
* map 的參數和返回值
* parseInt 的參數和返回值
* **parseInt(\*string\*, \*radix\*)** 解析一個字符串并返回指定基數的十進制整數, `radix` 是2-36之間的整數,表示被解析字符串的基數
```javascript
[10,20,30].map(parseInt) // [10, NaN, NaN]
// 拆解
[10,20,30].map((item,index)=>{
return parseInt(item,index)
})
```
#### ajax 請求 get 和 post 的區別
* get 一般用于查詢操作,post 一般用于提交操作
* get 參數拼接在 url 上,post 放在請求體內(數據體積可以更大)
* 安全性:post 易于防止 CSRF
### 再學閉包
#### 函數 call 和 apply 的區別
```javascript
// 傳參不同,call 參數分開傳,apply 傳一個數組
fn.call(this,p1,p2,p3)
fn.apply(this,arguments)
```
#### 事件代理(委托)是什么

#### 閉包是什么,有什么特性?有什么負面影響?
跳轉 [閉包](# 閉包)
閉包影響:變量會常駐內存,得不到釋放。閉包不要亂用
### 回顧 DOM 操作和優化
#### 如何阻止事件冒泡和默認行為?
```javascript
event.stopPropagation()
event.preventDefault()
```
#### 如何減少 DOM 操作?
```javascript
// DOM 查詢結果做緩存
// 創建一個文檔片段,document.createDocumentFragment()
```

### jsonp 本職是 ajax 嗎?
#### 解釋 jsonp 的原理,為何它不是真正的 ajax
* 瀏覽器的同源策略(服務端沒有同源策略)和跨域
* jsonp 原理

> jsonp 是使用 script 標簽來進行通信。沒有使用 xhr 請求。
#### document load 和 ready 的區別

#### == 和 === 的不同
跳轉 [鏈接](# == 運算符)
* 日常使用中 除了 == null 之外,其他都一律使用 ===
### 常見的正則表達式
#### 關于作用域和自由變量的場景題
```javascript
// question 1
let i
for(i=1;i<=3;i++){
setTimeout(function(){
console.log(i)
},0)
}
// answer1
4
4
4
```
```javascript
// question 2
let a = 100
function test(){
alert(a)
a = 10
alert(a)
}
test()
alert(a)
// answer 2
100
10
10
```
#### 判斷字符串以字母開頭,后面字母數字下劃線,長度 6-30
```javascript
const reg = /^[a-zA-Z]\w{5,29}$/
```
[正則表達式30分鐘入門教程](https://deerchao.cn/tutorials/regex/regex.htm)
### 如何獲取最大值
#### 手寫字符串 trim 方法,保證瀏覽器兼容性
```javascript
String.prototype.trim = function(){
return this.replace(/^\s+/,'').replace(/\s+$/,'')
}
// 原型、this、正則表達式
```
#### 如何獲取多個數字中的最大值
```javascript
function max() {
const nums = Array.prototype.slice.call(arguments) // 變為數組
let max = 0
nums.forEach((n) => {
if (n > max) {
max = n
}
})
return max
}
```
#### 如何用 JS 實現繼承
* class 繼承
* protorype 繼承
### 解析 url 參數
#### 如何捕獲 JS 程序中的異常?
```javascript
try{
// todo
} catch (ex) {
console.error(ex) // 手動捕獲 catch
} finally {
// todo
}
```
```javascript
window.onerror = function(message,source,lineNom,colNom,error){
// 第一,對跨域的 js,如 CDN 的,不會有詳細的報錯信息
// 第二,對于壓縮的 js,還要配合 sourceMap 反查到未壓縮代碼的行、列
}
```
#### 什么是 JSON
* json 是一種數據格式,本職是一段字符串
* json 格式和 js 對象結構一致,對 JS 語言更友好
* window.JSON 是一個全局對象:JSON.stringify JSON.parse
#### 獲取當前頁面 url 參數


### 數組去重有幾種方式
#### 將 url 參數解析為 JS 對象
```javascript
// 傳統方法
function queryToObj() {
const res = {}
const search = location.search.substr(1) // 去掉前面的 '?'
search.split('&').forEach((paramStr) => {
const arr = paramStr.split('=')
const key = arr[0]
const val = arr[1]
res[key] = val
})
return res
}
```
```javascript
// 新方法
function queryToObj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
```
#### 手寫數組 flatern,考慮多層級
> flatern 可以理解為,拍平,扁平化(v.)
```javascript
flat([1,2,[1,2,3],4,5])
// [1,2,1,3,4,5]
```
```javascript
function flat(arr) {
// 驗證 arr 中,還有沒有深層數組 [1,2,[3,4]]
const isDeep = arr.some((item) => item instanceof Array)
if (!isDeep) {
return arr // 已經是 flatern [1,2,3,4]
}
const res = Array.prototype.concat.apply([], arr) // 這東西還是沒看懂
return flat(res) // 遞歸
}
console.log(flat([1, 2, [3, 4, [5, 6, 7, [8, 9]]]]))
```
#### 數組去重
```javascript
// 傳統方式
function unique(arr) {
const res = []
arr.forEach((item) => {
if (res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
console.log(unique([1, 2, 2, 2, 11, 3, 3]))
```
```javascript
// 使用 Set(無序,不能重復)
function unique(arr){
const set = new Set(arr)
return [...set]
}
```
### 是否用過 requestAnimationFrame
#### 手寫深拷貝
[跳轉鏈接](# 深拷貝)
* Object.assign 不是深拷貝
```javascript
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// 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
}
```
#### RAF requestAnimationFrame
* 要想動畫流暢,更新頻率要 60幀/s,即16.67ms 更新一次視圖
* setTimeout 要手動控制頻率,而 RAF 瀏覽器會自動控制
* 后臺標簽或隱藏 iframe 中,RAF 會暫停,而 setTimeout 依然執行
#### 前端性能如何優化?一般從哪幾個方面考慮?
* 原則:多使用內存、緩存、減少計算、減少網路請求
* 方向:加載頁面,頁面渲染,頁面操作流暢度
*****
**完**
# 祝大家早日找到心儀的工作