> 下面的三级标题,代表的是此小节视频对应的标题。四级标题为对应的面试题。
### 何为变量提升
#### 变量提升
```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)
```
#### 事件代理(委托)是什么
![image-20210313095115799](https://image.mdashen.com/pic/image-20210313095115799.png)
#### 闭包是什么,有什么特性?有什么负面影响?
跳转 [闭包](# 闭包)
闭包影响:变量会常驻内存,得不到释放。闭包不要乱用
### 回顾 DOM 操作和优化
#### 如何阻止事件冒泡和默认行为?
```javascript
event.stopPropagation()
event.preventDefault()
```
#### 如何减少 DOM 操作?
```javascript
// DOM 查询结果做缓存
// 创建一个文档片段,document.createDocumentFragment()
```
![image-20210313100717748](https://image.mdashen.com/pic/image-20210313100717748.png)
### jsonp 本职是 ajax 吗?
#### 解释 jsonp 的原理,为何它不是真正的 ajax
* 浏览器的同源策略(服务端没有同源策略)和跨域
* jsonp 原理
![image-20210313101530622](https://image.mdashen.com/pic/image-20210313101530622.png)
> jsonp 是使用 script 标签来进行通信。没有使用 xhr 请求。
#### document load 和 ready 的区别
![image-20210313101634495](https://image.mdashen.com/pic/image-20210313101634495.png)
#### == 和 === 的不同
跳转 [链接](# == 运算符)
* 日常使用中 除了 == 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 参数
![image-20210318173641426](https://image.mdashen.com/pic/image-20210318173641426.png)
![image-20210318173718181](https://image.mdashen.com/pic/image-20210318173718181.png)
### 数组去重有几种方式
#### 将 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 依然执行
#### 前端性能如何优化?一般从哪几个方面考虑?
* 原则:多使用内存、缓存、减少计算、减少网路请求
* 方向:加载页面,页面渲染,页面操作流畅度
*****
**完**
# 祝大家早日找到心仪的工作