谈谈对ES6的理解
变量var、let 和 const 区别?
关于解构赋值?
模板字符串?
扩展运算符?
rest参数?
块级作用域?
箭头函数?
Promise、generator 和async?
for...of 和 for...in 的区别?
Set?
WeakSet?
Map?
WeakMap?
ES6 模块化如何使用,开发环境如何打包?
Class类?
1. 变量let 和 const 区别?
- 第一点: 「变量提升」方面
1. var: 「存在」变量提升,变量可在声明前调用,值为undefined
2. let和const: 「不存在」变量提升,一定要在声明后使用,否则报错(暂时性死区)
- 第二点: 「作用域」方面
1. var: 不存在块级作用域
2. let和const: 存在块级作用域
- 第三点: 「声明」方面
1. var: 统一作用域下,允许声明重复变量名
2. let和const: 统一作用域下,不允许声明重复变量名
- 第四点: [声明类型]方面
1. var: 可以声明「常量」和「变量」
2. let: 声明「变量」
3. const: 声明「常量」,一旦声明,它的值不会改变
// 问题: 如何使const声明的对象属性不可变?
const 声明了「对象」,其对象属性是可以改变的,因为const声明的对象只保存了其「地址」,地址不变,不会出错。
可以使用Object.freeze()递归一层层冻结
2. 关于解构赋值?
-
作用: 一种提取数据的模式,写法简洁,减少代码量
-
A.「数组」的解构赋值: 以元素的位置「下标」匹配
const [a, b] = ['0', '1']
数据交换
console.log(a, b)
- B.对象的解构赋值
const {a, b} = {a: '0', b: '1'}
console.log(a, b)
3.模板字符串?
- 定义: 增强版的 「字符串」,用反引号``标识,可以在字符串中嵌入「变量」
- 作用:
1. 支持换行,不需要转义符
2. 反引号``标识,可以嵌入 变量
4. 块级作用域?
5. 扩展运算符?
- (...)定义:将「数组」变成以「空格」分隔的参数序列
- (...)作用:
// 第一点: 复制(拷贝):
1. 「数组」的复制
数组中存放的是「基本数据类型」 实现的是「深拷贝」
数组中存放的是「引用数据类型」 实现的是「浅拷贝」
arr2 = [...arr1]
2.「对象」的复制
对象中存放的是「基本数据类型」 实现的是「深拷贝」
对象中存放的是「引用数据类型」 实现的是「浅拷贝」
obj2 = {...obj1}
// 第二点: 合并
1.「数组」的合并
var arr1 = [1, 2], var arr2 = [3]
var arr3 = [...arr1, ...arr2]
2. 「对象」的合并
var obj1 = {a: '11'}, var obj2 = {b: '2'}
var obj3 = {...obj1, ...obj2}
// 第二点:字符串改为数组
var test = [...'he']
结果['h', 'e']
6. rest参数?
- 作用: 获取函数多余的参数,和扩展运算符结合使用
- rest 参数只能放在最后一个, 不然就会报错
function(...argus) {
console.log(argus)
}
fun(1, 2, 3)
7. 箭头函数?
面试:箭头函数和普通函数的区别?
- 第一点: this指向的区别
// 箭头函数
1. 箭头函数中的 this 是在函数定义时就决定的,指向定义的时候,外层第一个普通函数的this,不可修改(call, bind, apply),
- 第二点: 是否可以被new
// 箭头函数
1. 箭头函数 不可以 「new」(不能当作构造函数)
2. 箭头函数没有「prototype」
- 第三点: 关于arguments
// 箭头函数
1. 箭头函数 没有 「arguments」
8. Promise、generator 和async?
面试1: Promise有几种状态?
三种状态:
Pending: 进行中
Fulfilled: 已成功
Rejected: 已失败
面试2: Promise用于解决什么问题?
1. 「异步编程」的解决方案
2. 解决了「回调地狱」(回调地狱,维护性差,可读性差)
问题: .then 层层递进,写法不够简洁
面试3: Generator解决什么问题?
- '*'代表generator函数
- yield
- .next()访问
1. 「异步」的解决方案
2. 重点解决「Promise」.then 写法复杂的问题
问题:写法还是不够简洁,所以出现async
面试3: async和await解决什么问题 ?
1. 将promise写法更加简单化, 「异步编程」最佳解决方案
2. 异步代码交给Promise 包裹
3. async和await真正做到了同步写法
4. 缺点:无法处理处理promise 返回的rejected对象,要借助try...catch...
面试4: async和await原理?
1. 是generator 封装的一个语法糖
面试5: 手写实现Promise、Promise.all 和 Promise.race?
- Promise
1. Promise 是一个类
2. executor是一个执行器,同步执行,传入两个函数
3. resolve方法执行成功的结果,状态变为fulfilled
4. rejected方法执行失败原因,状态变为rejected
5. 定义then方法,两个参数,处理成功的,处理失败的
6. 加限制,因为状态不可逆if (this.status == PENDING)
7. 处理executor 内部错误 try ... catch
8. 处理异步
9. 思考? -处理链式调用
class myPromise() {
// 定义三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
constructor(executor) {
this.states = PENDING // 默认的状态
this.value = '' // 成功的结果
this.reason = '' // 失败的原因
this.onFulfilledCallbacks = []; // 成功回调集合
this.onRejectedCallbacks = []; // 失败的回调集合
const resolve = (value) => {
if (this.status == PENDING) { // 状态唯一性
this.status = FULFILLED
this.value = value
// 调用resolve 的时候遍历函数
this.onFulfilledCallbacks.forEach((cb) => cb() )
}
}
const reject = (reason) => {
if (this.status == PENDING) { // 状态唯一性
this.status = REJECTED
this.reason = reason
// 调用rejected的时候遍历
this.onRejectedCallbacks.forEach((cb) => cb())
}
}
<!--捕获executor错误-->
try {
executor(resolve, reject)
} catch(error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
// onFulfilled 成功的处理函数
// onRejected 失败的处理函数
// 成功
if (this.status === FULFILLED) {
// 微任务中
queueMicrotask(()=>{
onFulfilled(this.value);
})
}
// 失败
if (this.status === REJECTED) {
// 微任务中
queueMicrotask(()=>{
onRejected(this.reason);
})
}
// 处理异步
if (this.status === Pending) {
this.onFulfilledCallbacks.push(()=>{
queueMicrotask(()=>{
onFulfilled(this.value);
})
})
this.onRejectedCallbacks.push(() => {
queueMicrotask(()=>{
onRejected(this.reason);
})
})
}
}
}
const p1 = new myPromise((resolve, reject) => {})
p1.then((res) => {})
- Promise.all
<!--方法1: for 循环-->
1. 参数,接收数组
2. 返回新的Promise实例
3. 设置一个数组用于接收所有结果(要按照顺序)
4. 设置一个计数器,用来判断循环是否结束
4. 对参数进行遍历
5. 判断元素是不是Promise实例
6. 判断结束的标识
Promise._all = function(array) {
return new Promise((resolve, reject) => {
let result = []
let count = 0
for(let i = 0; i < array.length; i++) {
const item = array[i]
if (item.constructor === Promise) {
item.then(data => {
result[i] = data
count++
if (count === array.length) {
resolve(result)
}
).catch(() => {
reject()
})
} else {
result[i] = item
count++
}
}
)
}
<!--方法2: 递归方法, 等前一个结束后,再执行下一个-->
Promise._all = function (array) {
return new Promise((resolve, reject) => {
let result = []
next(0)
function next(i) {
let item = array[i]
if (!item) {
resolve(result)
return
}
if (item instanceof Promise) {
item.then(data => {
result.push(data)
next(i+1)
}, reject)
} else {
result.push(item)
next(i+1)
}
}
})
}
- Promise.race
1. 参数,接收数组
2. 返回Promise实例
Promise._race = function (array) {
return new Promise((resolve, reject) => {
for(let i = 0; i < array.length; i++) {
const item = array[i]
if (item.construct === Promise) {
item.then(data) => {
resolve(data)
}, err => {
reject(err)
}
} else {
resolve(item)
}
}
})
}
9. for...of 和 for...in 的区别?
- for...of:可遍历「数组」、「对象」、「字符串」、「Set数据结构」
// 遍历对象:
1. 遍历,获取 「对象」的「键值」
2. 遍历, 只会遍历「当前对象」
3. 性能稍好
// 遍历数组:
1. 遍历, 返回下标对应的「属性值」
- for...in: 主要为了遍历「对象」,不适用「数组」
// 遍历对象:
1. 遍历,获取「对象」的「键名」
2. 遍历, 会遍历该对象的「整个原型链」
3. 所以性能稍差
// 遍历数组:
1. 遍历,返回所有「可枚举」的「属性」
10. Set?
数组: 元素是「不唯一可以重复」,「有序的」,通过下标、索引快速找到某个元素
- Set解决什么问题?
1. 定义:Set数据结构,它存储的是-组「唯一的」「无序」的元素集合
- Set构造函数声明?
const set = new Set()
const set1 =new Set([1, 2]) // 初始化
- Set构造函数原型上的属性或方法?
const set = new Set()
1. 大小: set.size
2. 新增: set.add(value)
3. 删除: set.delete(value) // 返回boolean
4. 判断: set.has(value) // 返回boolean
5. 清空: set.clear()
- Set构造函数,遍历的方法?
const set = new Set(['red','blue'])
1. set.entries()
2. set.keys()
3. set.values()
4. set.forEach()
1. 遍历「键值对」
for(let item of set.entries()) {
console.log(item)
// ['res', 'red'],['blue','blue']
}
2. 遍历「键」
for (let key of set.keys()) {
console.log(key) // red, blue
}
3. 遍历「值」
for(let value of set.values()) {
console.log(values) // res, blue
}
- Set 和数组的互转?
const arr = [1, 2]
// 数组 转化成 Set
1. const set = new Set(arr)
// Set 转化成 数组
1.扩展运算符: const arr1 = [...new Set()]
2. Array.from: const arr2 = Array.from(new Set())
- Set 实际场景应用?
var set1 = [1, 2, 3, 4]
var set2 = [2, 3, 4, 5]
1. 数组去重: arr1 = [...new Set()]
2. 求两个集合的并集、交集和差集
a. 并集:new Set([...set1, ...set2])
b. 交集:[...set1].filter(a => {set2.has(a)})
c. 差集:[...set1].filter(a => {!set2.has(a)})
11. WeakSet?
- WeakSet 定义
1. 和 Set 相同点: 存储「唯一的」「无序」元素
2. 区别1:WeakSet 成员只能是「引用类型」
3. 区别2: 不可被遍历, 不计入「垃圾回收机制」
- WeakSet构造函数, 原型上的属性?
const set = new WeakSet()
1. 添加: set.add()
2. 删除:set.delete()
3. 判断:set.has()
- WeakSet不可遍历
12. Map?
关于对象: 本质是「键值对」集合(Hash结构),键: 只能是字符串(若不是,内部通过toString转化成字符串)
- Map 解决什么问题?
1. 定义:Map构造函数, 类似于对象,也是「键值对」集合
2. 解决:「健」可以是「任意数据类型」
- Map构造函数声明?
const map = new Map()
- Map构造函数,原型上的属性或方法?
const map = new Map()
// 增、删、改、查
1. 新增:map.set(key, value).set(key, value)
2. 删除:map.delete(key), 返回true or false
3. 取值:map.get(key)
4. 清空:map.clear()
5. 判断是否存在某个key:map.has(keys)
6. 数量: map.size
- Map构造函数,遍历方法?
1. map.entries()
2. map.keys()
3. map.values()
m4. ap.forEach(), 与数组forEach类似
const map = new Map
1. 遍历整个「键值对」
for (let [key, value] of map) {}
for(let item of map.entries())
2. 遍历「键」
for(let key of map.keys()){}
3. 遍历「值」
for(let value of map.values())
13. WeakMap?
- WeakMap的定义
1. 定义:WeakMap构造函数,也是「键值对」集合
2. 区别1:「键」只能是「对象」,null除外
3. 区别2: 不可被遍历,WeakMap「键名」指向的对象,不计入「垃圾回收机制」
- WeakMap构造函数声明?
const map = new WeaMap()
- WeakMap 原型上的方法?
const map = new WeakMap()
// 增、删、改、查
1. 新增: map.set()
2. 删除: map.delete()
3. 获取: map.get()
4. 判断: map.has()
- WeakMap 没有遍历操作(entries、keys、values、size)
14. ES6 模块化?
- 语法
// 1.
export default {
ceshi: '---'
}
import ceshi from './..'
// 2.
export function a() {
}
export function b() {
}
import {a, b} from './..'
- 编译: babel 定义: ES6转码器: 将ES6代码转化成浏览器识别的代码
1.根目录新建 ‘.babelrc’文件,
{
"preset": ["es2015", "latest"],
"plugins": []
}
2. 安装:
babel-core
babel-preset-es2015
babel-preset-latest
babel-polyfill
- 开发环境 webpack
1. 安装 webpack 和babel-loader
2. 新建‘webpack.config.js’
export = {
entry:,
output:,
module: {
rules: []
},
}
- 开发环境 rollup
1. 安装:
rollup
rollup-plugin-node-resolve
rollup-plugin-babel
babel-plugin-external-helper
babel-preset-latest
2. rollup 功能单一, webpack功能强大
3. 新建 ‘rollup.config.js'
export default {
entry: '',
format: 'umd',
plugins: [
resolve()
]
}
- 模块化发展
1.没有标准
2.AMD标准 require.js
3.前端打包工具, nodejs模块化
4. ES6统一模块化标准
5. nodejs 积极支持,尚未统一
15. Class?
js面向对象, 生成实例对象的传统方法是通过「构造函数」
- Class 由来?
1. 更符合平时理解的「面向对象」的语言
2. class 是一个语法糖,其底层还是通过「构造函数」去创建的
- Class和普通构造函数的区别?
1. 第一点: Class不能提升,普通函数可以变量提升
2. 第二点: Class只能通过new 实例化
3. 第三点: Class采用「严格模式」
4. 第四点: Class 原型上的属性,不能遍历
5. 第五点: 在Class内部,不可以修改class名称
- constructor
1. constructor():是类的构造函数,通过new 创建对象实例时, 会自动调用
- super
作用:
1. 当函数使用: super(), 子类没有自己的this对象,super代表了父类的构造函数
2. 当作对象使用: super 在普通方法中,指向「父类.prototype」, 绑定的确是子类的「this」
面试1: 当我们通过new 调用构造函数,都干了啥?
1. 在内存中创建「空的对象」
2. this就会指向刚才创建的「空对象」
3. 执行构造函数里面的代码,给改「空对象」添加属性和方法
4. 隐式返回「该对象」,构造函数不需要return
面试2: Class实现私有属性?
- 私有属性
1. 能被class内部的不同方法访问,但不能在类外部被访问
2. 子类不能继承父类的私有属性
- 解决方案:
// 闭包:
class Private() {
constructor(x) {
let a = x
this.getA = function() {
return a
}
}
}
let obj = new Private(1)
console.log(obj.a) // undefined
console.log(obj.getA) // 1