1 新的变量声明方式
ES6中,使用let和const代替var定义变量。let用于定义一个变量,const用于定义一个常量,其中const定义的对象只是对象的地址不变,对象的属性是可以增加或者删除的。
使用var定义变量时,会有一些问题,如变量声明提前,ES6中禁止了。
1 | //ES5 |
var定义变量只存在全局作用域和函数作用域,其他大括号中不存在块级作用域,let&const定义的变量在所有大括号中都存在块级作用域。
1 | //ES5 |
使用let&const的特点:
禁止变量声明提前
增加了块级作用域,任何大括号都可以形成块级作用域
const定义的对象属性值依然可以改变,但地址不能改变。
let&const定义的全局变量不作为window的变量
2 解析结构
1 | let [a,b,c]=[ |
解析结构可以让我们快速的将接口返回的数据赋值到变量上。
同时,只要数组的下标对应或对象的键名对应都可以实现解析结构
1 | //数组的下标对应,将第一个跟第三个取出并赋值给a、c变量 |
3 字符串
3.1 模板字符串
模板字符串使用反单引号包围,在字符串中可以使用变量,通过${ }的形式插入变量
1 | //ES5 |
3.2与字符串有关的API
ES6中引入了一些新的处理字符串的API
1 | let str='lilei'; |
startsWith()接收一个字符串,判断调用该方法的字符串是否以接收的字符串参数作为开头,endsWith()接收一个字符串,判断调用该方法的字符串是否以接收的字符串参数作为结尾。
4 函数默认值
函数的参数可以预设默认值
1 | function showNumber(number=0) { |
5 箭头函数
实现调用一次obj.addCount方法就使obj.count加一。
1 | //ES5 |
箭头函数就是将function(){}改写为()=>,箭头函数内的this指向外层的this指向的对象,这里是addCount函数的作用域中的this指向的obj对象,这是箭头函数最重要的特性。
箭头函数内只有一条js语句时,可以省略大括号
1 | let fun=()=>{console.log('Hello World')}; |
6 对象字面量简写
6.1 对象字面量中赋值简写
对象字面量中相同键名与键值同名的情况可以简写。
1 | //ES5 |
6.2 对象字面量中声明函数简写
1 | //ES5 |
6.3 动态键名
使用中括号表示动态的键名
1 | let key='name'; |
7 Promise
可能你还没有遇到过回调地狱,但是JS作为一门异步编程的语言,学习Promise十分重要,它可以让我们优雅的编写异步程序
https://juejin.im/post/5d6cc40bf265da03ca1186fd
Promise 对象有三种状态:
1.pending:刚刚创建一个 Promise 实例的时候,表示初始状态;
2.fulfilled:resolve 方法调用的时候,表示操作成功;
3.rejected:reject 方法调用的时候,表示操作失败;
then()函数
Promise实例生成以后,可以用then()方法分别指定resolved状态和rejected状态的回调函数,用于绑定处理操作后的处理程序。
1 | promise.then(function(value) { |
then()方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
说简单点就是参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作异常后的业务。
1 | // 首先用 new 关键字创建一个 `Promise` 实例 |
上面示例介绍了从 创建实例,状态转换,then方法和catch方法的使用。
如果多个操作之间层层依赖,我们用Promise又是怎么处理的呢?
1 | const promise = new Promise(function(resolve, reject){ |
输出结果:

上面的代码,先是创建一个实例,还声明了4个函数,其中三个是分别代表着请求A,请求B,请求C;有了then方法,三个请求操作再也不用层层嵌套了。我们使用then方法,按照调用顺序,很直观地完成了三个操作的绑定,并且,如果请求B依赖于请求A的结果,那么,可以在请求A的程序用使用return语句把需要的数据作为参数,传递给下一个请求,示例中我们就是使用return实现传递参数给下一步操作的。

再举个Promise 中微任务顺序的栗子1:
1 | var p = new Promise( (resolve, reject) => { |
如上示例解释:
1.先执行new Promise第一层的代码,遇到setTimeout,将其推入宏任务队列中(此时未执行,排在当前script代码块的宏任务之后执行),然后遇到了resolve,执行Promise后面的代码。
2.遇到.then 1-1,推入微任务队列里(只是推入,并未执行,所以.then 1-2的执行时机还没有到),这个时候发现没有其他的操作需要处理(比如推其他的微任务到队列里),那么就执行当前微任务队列里的函数,也就是执行.then 1-1的回调函数。
3.执行.then 1-1的回调函数的时候,发现了里面有一个完成态的Promise对象,不用管继续走,遇到了.then 2-1,推入微任务队列(只是推入,并未执行),此时.then 1-1回调执行完毕(没有return值,相当于return了一个undefined),然后Promise得以继续往下执行,遇到了.then 1-2,继续推入微任务队列(依然没执行),这时发现没有其他操作,开始顺位执行当前微任务队列里的函数(此时微任务队列里存放了.then 2-1和.then 1-2的回调函数),执行.then 2-1的回调函数时,又遇到了一个完成态的Promise,不用管继续走,遇到了.then 3-1,将其推入微任务队列(未执行),然后执行.then1-2的回调,打印 3 ,此时已经没有了其他的操作,所以继续执行微任务队列里剩余的函数,即.then 3-1的回调函数,打印 2 。
4.至此,微任务队列已经执行完毕,开始执行宏任务队列中的下一个宏任务,打印 1 。


Promise常用类方法
Promise.all( ) 方法:
应用场景:我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作。
注意:如果传入的 promise 中有一个失败(rejected),Promise.all异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。
1 |
|
Promise.finally( ) 方法
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。这避免了同样的语句需要在then()和catch()中各写一次的情况。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
Promise.race( ) 方法
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
Promise.resolve( ) 方法
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
Promise.resolve()等价于下面的写法。
Promise.resolve(‘foo’)
// 等价于new Promise(resolve => resolve(‘foo’))
上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。
Promise.resolve()等价于下面的写法。
Promise.resolve(‘foo’)
// 等价于new Promise(resolve => resolve(‘foo’))
Promise.resolve方法的参数分成四种情况。
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
1 | let thenable = { |
上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
1 | const p = Promise.resolve('Hello'); |
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。
(4)不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。
const p = Promise.resolve();
p.then(function () {
// …});
上面代码的变量p就是一个 Promise 对象。
需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
1 | setTimeout(function () { |
上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log(‘one’)则是立即执行,因此最先输出。
Promise.reject( ) 方法
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
1 | const p = Promise.reject('出错了'); |
上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。
注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
1 | const thenable = { |
上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。
8 类与继承 TODO
https://juejin.im/post/5cfc64736fb9a07f0c4674ba
ES6中提供了class关键字与extends关键字让我们方便快捷的书写OOP代码
在Javascript中,有两种属性:
对象.属性(实例属性),属于某个对象的属性
类.属性(静态属性),静态属性是所有对象公有的属性
ES6中提供了一个关键字class允许我们声明一个类,通过关键字extends来实现类的继承,ES6中的类是一个语法糖,本质上还是由ES5的语法实现的,下面我们来详解ES6中的类与继承。
类
1.构造函数
先来看一下ES5中是如何定义一个类的
1 | //ES5 |
ES6中,使用关键字class声明一个类,在类中constructor函数是一个构造函数,用法与ES5类似。
注意:class跟let/const一样,是不存在变量声明提前的,调用class必须放在声明class之后。constructor函数若未定义,会自动生成一个方法体为空的constructor函数。
2 公有方法
ES5中的共有方法是定义在原型链上的,ES6则是直接写在类中,但它的原理依然是定义在原型链上的。
1 | //ES5 |
3静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
静态方法只能在静态方法中调用,不能在实例方法中调用。
静态方法是使用类名.方法名调用的,在JS中一切皆对象的思想,方法也不例外,方法也是一个对象,那么定义静态方法的方式就很简单了,下面看例子。
1 | //ES5 |
ES6中class内只允许声明静态方法,不允许声明静态属性。使用关键字static声明静态方法。静态属性的声明方式与ES5的声明方式相同。
继承
ES5中的继承首先要执行父类方法,然后将原型对象指向父类的原型对象,最后修正原型对象的constructor属性,使其指回子类构造函数,比较麻烦,ES6中采用关键字extends实现继承,使用super调用父类的构造函数。
1 | //ES5 |
9 export与import
在ES5的时候,在同一个html文件中引入两个js文件来实现js跨文件的引用。
ES6中,将每一个文件视为一个模块,在一个模块中可以通过export关键字设置允许外部文件访问的变量。其他模块通过import关键字将其他js模块导入当前模块中。下面直接看例子:
1 | //a.js |
a.js中name被设置对外允许访问,而_age没有设置为对外允许访问。
1 | //b.js |
export第三种写法是使用export default:
1 | //a.js |
前面我们使用的import都是对模块暴露出来的变量一个个接收的,也可以一次接收多个变量
1 | //a.js |
export default语句只能写一句,export语句可以写多句,export default只能输出一个变量,接收的时候不需要包含在大括号内。
注意:export与import在浏览器上还不支持,可以在一些打包工具如webpack中使用。
10 assign
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
1 | const target = { a: 1, b: 2 }; |
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
注意,Object.assign 不会在那些source对象值为 null 或 undefined 的时候抛出错误。