# 三.ES6 知识点
# 1.var、let 及 const 区别
什么是提升?什么是暂时性死区?var、let 及 const 区别?
变量声明在函数之后,函数会被提升,并且优于变量提升
在全局作用域下let和const声明变量,变量不会被挂载到window上,再次声明之前声明的变量会报错,在声明之前使用变量也会报错(报错的原因就是暂时性死区)
# 2.原型继承和 Class 继承
原型如何实现继承?Class 如何实现继承?Class 本质是什么?
其实在 JS 中并不存在类,class只是语法糖,本质还是函数
calss Person{
}
Person instanceof Function // true
2
3
4
# 1.组合继承
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function () {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child()
child.getValue() // 1
child instanceof Parent // true
2
3
4
5
6
7
8
9
10
11
12
13
14
以上继承的方式核心是在子类的构造函数中通过Parent.call(this)继承父类的属性,然后改变了子类的原型为new Parent()来继承父类的函数。
这种继承方式的优点在于构造函数可以传参,不会与父类引用属性共享,可以复用父类的函数,但是也存在一个缺点就是在继承父类函数的时候调用了父类的构造函数,导致子类的原型上多了不需要的父类的属性,存在内存上的浪费。
# 2.寄生组合继承
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function () {
console.log(this.val)
}
function Child(value) {
Parent.call(this.value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true,
},
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent //true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
以上继承实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。
# 3.Class 继承
以上两种继承方式都是通过原型去解决的,在 ES6 中,我们可以使用class去实现继承,并且实现起来很简单
class Parent {
constructor(value) {
this.value = value
}
getValue(value) {
console.log(value)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() //1
child instanceof Parent // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
calss实现继承的核心在于使用extends表明继承来自哪个类,并且在子类构造函数中必须调用super,因为这段代码可以看成Parent.call(this,value)
当然了,之前也说了在 JS 中并不存在类,class的本质就是函数。
# 3.模块化
为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?
- 基本概念:
- 1.在 js 中,一个模块就是实现特定功能的文件(js)文件
- 2.遵循模块的机制,想要什么就加载什么模块
- 3.模块化开发需要遵循规范
- js 实现模块化规范
- 1.AMD 浏览器 requirejs 模块被异步加载,模块加载不影响后面语句的运行,默认使用 baseURL+paths 的路径解析方式
- 2.CommonJS nodejs
- 3.ES6 的 import/export
- 4.CMD 浏览器端
- 解决的问题:
- 1.命名冲突
- 2.文件依赖
- 3.模块的复用
- 4.提高代码可维护性,统一规范和开发方式
# 1.立即执行函数
在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题
;(function (global) {
global.test = function () {}
})(gloabl)
2
3
# 2.AMD 和 CMD
# 3.CommonJS
# 4.ES Module
ES Module 是原生实现的模块化方案,与 CommonJS 有以下几个区别
- CommonJS 支持动态导入,也就是
require(${path}/x.js),后者目前不支持,但是已有提案 - CommonJS 是同步导入,因为用于服务器,文件都在本地,同步导入即使卡主主线程影响也不大。后者是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响。
- CommonJS 在导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以如果想要更新值,必须重新导入一次。但是 ES Modlues 采用实时绑定的方式,导入导出的值都指向同一个内存地址,所以导入值会跟随导出值变化
- ES Modules 会编译成
require/exports来执行
# 4.Proxy
Proxy 可以实现什么功能?
# 5.Set、Map
- Set
- 成员的值都是唯一的,没有重复的值
- 两个属性 constructor,size
- 可以遍历
- 四个方法:add,delete,has,clear
- weakSet
- 不重复的值的集合
- 成员只能是对象,不计入垃圾回收机制
- 没有 size 属性,不能遍历
- 三个方法 add,delete,has
- Map
- javascript 的对象,本质上是键值对的集合
- 但是传统上只能用字符串当做键
- 可以遍历,方法很多,可以干根各种数据格式转换
- weakMap
- 只接受对象作为键名
- 键名指向的对象,不计入垃圾回收机制
- 不能遍历,方法同 get,set,has,delete
# 6.异步流程
# 6.1 Generator
Generator最大的特点就是可以控制函数的执行。
# 6.2 Promise
Promise 的特点是什么,分别有什么缺点?什么是 Promise 链?Promise 构造函数执行和 then 函数执行有什么区别?
promise 有三种状态:pending、resolved、rejected 这个承诺一旦从等待状态变为其他状态就永远不能更改状态了,也就是说一旦状态变为 resolved 后,就不能再改变
当我们在构造 promise 的时候,构造函数内部的代码是立即执行的
Promsie 实现了链式调用,很好地解决了回调地狱的问题,但是也有一些缺点比如无法取消 Promise,错误需要通过回调函数捕获。
promise
- 1.是一个对象,用来传递异步操作的信息。代表着某个未才会知道结果的时间,并为这个事件提供统一的 api,供其进行异步处理
- 2.有了这个对象,就可以让异步操作以同步的操作的流程来表达处理,避免层层嵌套的回调地狱
- 3.promise 代表一个异步状态,有三个状态 pending(进行中),Resolve(已完成),reject(失败)
- 4.一旦状态改变,就不会再变。任何时候都可以得到结果。从进行中变为已完成或失败
- promise.all()里面的状态都改变,那就会输出,得到一个数组
- promise.race()里面只有一个状态变为 rejected 或者 fulfilled 即输出
- promise.finally()不管指定 pormise 对象最后的状态如何,都会执行的操作(本质上还是 then 方法的特例)
# 6.3 async 及 await
- 一个函数如果加上 async,那么这个函数就会返回一个 Promise
- async 就是将函数返回值使用
Promise.resolve()包裹了下,和then中处理返回值一样,并且await只能配套async用 async和await可以说是异步终极解决方案了,相比直接使用Promsie来说,优势在于处理then的调用链,能够更清晰准确的写出代码,毕竟写一大堆then也很恶心,并且也能优雅的解决回调地狱问题。当然也存在一些缺点。因为await将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了await会导致性能上的降低。- 其实
await就是generator加上Promise的语法糖,且内部实现了自动执行generator。如果你熟系 co 的话,其实自己可以实现这样的语法糖。