ES6学习笔记

1.let和const

es6中新增重要的两个js关键字:letconst

  • let :变量只在 let 命令所在的代码块内有效,块级。
  • const :常量,一旦声明,常量的值就不能改变,块级。
1
2
3
4
5
6
7
8
9
10
var arr=[]
for(var i =0;i<2;i++) {
console.log(i)
arr[i]=function(){
console.log(i)
}
}
arr[0]();
arr[1]();
console.log('last i='+i)
  • 这段函数for循环执行完后才会执行下面的arr[0]();arr[1]();但是此时的i已经变成了2,所以再去执行的时候,i的值都是2

image 20200725171153184

1
2
3
4
5
6
7
8
9
10
var arr=[]
for(let i =0;i<2;i++) {
console.log(i)
arr[i]=function(){
console.log(i)
}
}
arr[0]();
arr[1]();

image 20200725170623881

2.Promise

ECMAscript 6 原生提供了 Promise 对象。

Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息

Promise 对象有以下两个特点:

1、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
  • pending: 初始状态,不是成功或失败状态
  • fulfilled: 意味着操作成功完成
  • rejected: 意味着操作失败
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise 优缺点

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

一个最简单的Promise例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var p = new Promise (function(resolve,reject) {
if (true) {
resolve("完成")
}else{
reject("失败")
}
});
console.log(p)

p.then(function(res){
console.log(res)
},function(err){
console.log(err)
})

再来一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const promise1 = new Promise((resolve, reject) => {
setTimeout(function(){
console.log('sadfsdd')
if(true){
console.log('成功了')
resolve('最后成功')
}else{
console.log('失败了')
reject('最后失败了')
}
},1000)
})
promise1.then(function(res){
console.log(res)
}).then(()=>{
setTimeout(()=>{
console.log('sjw')
},2000)
}).then(()=>{
setTimeout(()=>{
console.log('syj')
},1000)
}).catch((res)=>{
console.log(res)
})
console.log('1', promise1);

image 20200726170543436

这有几个简单的题目,可以理解Promise的执行过程

题目一

1
2
3
4
const promise1 = new Promise((resolve, reject) => {
console.log('promise1')
})
console.log('1', promise1);

过程分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
  • 然后执行同步代码1,此时promise1没有被resolve或者reject,因此状态还是pending

结果:

1
2
'promise1'
'1' Promise{<pending>}

题目二

1
2
3
4
5
6
7
8
9
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve('success')
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);

过程分析:

  • 从上至下,先遇到new Promise,执行其中的同步代码1
  • 再遇到resolve('success'), 将promise的状态改为了resolved并且将值保存下来
  • 继续执行同步代码2
  • 跳出promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列
  • 执行同步代码4
  • 本轮宏任务全部执行完毕,检查微任务队列,发现promise.then这个微任务且状态为resolved,执行它。

结果:

1
1 2 4 3

题目三

1
2
3
4
5
6
7
8
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);

过程分析

  • 和题目二相似,只不过在promise中并没有resolve或者reject
  • 因此promise.then并不会执行,它只有在被改变了状态之后才会执行。

结果:

1
1 2 4

题目四

1
2
3
4
5
6
7
8
9
const promise1 = new Promise((resolve, reject) => {
console.log('promise1')
resolve('resolve1')
})
const promise2 = promise1.then(res => {
console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

过程分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
  • 碰到resolve函数, 将promise1的状态改变为resolved, 并将结果保存下来
  • 碰到promise1.then这个微任务,将它放入微任务队列
  • promise2是一个新的状态为pendingPromise
  • 执行同步代码1, 同时打印出promise1的状态是resolved
  • 执行同步代码2,同时打印出promise2的状态是pending
  • 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它。

结果:

1
2
3
4
'promise1'
'1' Promise{<resolved>: 'resolve1'}
'2' Promise{<pending>}
'resolve1'

题目五

接下来看看这道题:

1
2
3
4
5
6
7
8
const fn = () =>(new Promise((resolve, reject) => {
console.log(1);
resolve('success')
}))
fn().then(res => {
console.log(res)
})
console.log('start')

这道题里最先执行的是'start'吗 🤔️ ?

请仔细看看哦,fn函数它是直接返回了一个new Promise的,而且fn函数的调用是在start之前,所以它里面的内容应该会先执行。

结果:

1
2
3
1
'start'
'success'

题目六

如果把fn的调用放到start之后呢?

1
2
3
4
5
6
7
8
9
const fn = () =>
newPromise((resolve, reject) => {
console.log(1);
resolve("success");
});
console.log("start");
fn().then(res => {
console.log(res);
});

是的,现在start就在1之前打印出来了,因为fn函数是之后执行的。

注意⚠️:之前我们很容易就以为看到new Promise()就执行它的第一个参数函数了,其实这是不对的,就像这两道题中,我们得注意它是不是被包裹在函数当中,如果是的话,只有在函数调用的时候才会执行。

答案:

1
2
3
"start"
1
"success"

更多Promise面试题可见

看进来

3.解构赋值

1.数组解构

1
2
3
4
5
6
7
let arr = [1,2,3]

let [a,b,c] = arr//一次性对abc三个赋值

console.log(a)//1
console.log(b)//2
console.log(c)//3

注意:如果一个变量有默认值,若赋值时赋给它undefined,值并不会被修改为undefined

1
2
3
4
5
let [a,b,c="3"]=["1","2",undefined]

console.log(a)//1
console.log(b)//2
console.log(c)//3,并不是undefined

2.对象解构

对象解构允许我们使用变量的名字匹配对象的属性匹配成功将对象属性的值赋值给变量

1
2
3
4
5
6
7
8
let person = {name: 'Song' ,age: '22'}
let {name,age} = person
console.log(name)//Song
console.log(age)//22

let {name:MyName,age:MyAge}=person
console.log(MyName)//Song
console.log(MyAge)//22

注意:如果在被赋值的变量在赋值前已经被定义,那么再去结构的时候就会报错

1
2
3
4
5
6
let me={
name:"Song",
};
let name="Jun";
{name} = me
console.log(name);//会报错

此时可以用圆括号来解决这个问题()

1
2
3
4
5
6
let me={
name:"Song",
};
let name="Jun";
({name} = me);
console.log(name);//Song

4.箭头函数

箭头函数是用来简化函数定义语法

1
2
3
const sum = (a,b)=> a+b

console.log(sum(9,9))//18

如果形参只有一个可省略小括号

1
2
const fn = x => console.log(x)
fn(99)//99

箭头函数中不绑定this关键字,箭头函数中的this指向的是函数定义位置的上下文d的this

1
2
3
4
5
6
var obj = {
age: 20,
say: () => console.log(this.age)
}
obj.say()
//undefined
obj是一个对象,对象不会产生作用域,于是,this指向了全局Window的this,此时的this中并没有定义age
1
2
3
4
5
6
7
var age =22
var obj = {
age: 20,
say: () => console.log(this.age)
}
obj.say()
//22

5.剩余参数

将剩余的元素放在数组中

1
2
3
4
5
6
7
const sum = (...args) =>{
let total = 0;
args.forEach(item => total +=item);
return total;
}
console.log(sum(10,20))//30
console.log(sum(10,20,30))//60
剩余参数配合解构赋值使用
1
2
3
4
let person = ['aaa','bbb','ccc'];
let [s1,...s2] = person;
console.log(s1)//'aaa'
console.log(s2)//['bbb','ccc']

6.对象扩展

将数组或者对象转为用都好分隔的参数序列

1
2
3
4
let ary = ["a","b","c"];

console.log(...ary)//a b c
console.log("a","b","c")//a b c

合并数组

1
2
3
4
5
let ary1 = [1,2,3]
let ary2 = [4,5,6]
ary1.push(...ary2)
console.log(ary1)
//[1, 2, 3, 4, 5, 6]

将伪数组转换为真正的数组

1
2
3
4
5
6
7
var oDivs = document.getElementsByTagName('div')
console.log(oDivs)
var ary = [...oDivs]
console.log(ary)
ary.push('5')
console.log(ary)

image 20200725182945461

7.模板字符串

模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。

1
2
3
4
let url='users'
let URL=`http://123.com/${url}`
console.log(URL)
//http://123.com/users

8.子串的识别

ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法。

  • **includes()**:返回布尔值,判断是否找到参数字符串。
  • **startsWith()**:返回布尔值,判断参数字符串是否在原字符串的头部。
  • **endsWith()**:返回布尔值,判断参数字符串是否在原字符串的尾部。
1
2
3
4
5
let string = "apple,banana,orange";
string.includes("banana"); // true
string.startsWith("apple"); // true
string.endsWith("apple"); // false
string.startsWith("banana",6) // true