let && const
let定义变量,const定义常量
- 不存在变量提升
- 暂时性死区
- 不允许重复声明
- 块级作用域
- 不属于顶层对象window
- const:值不得改变,一旦声明,必须立即初始化
- const:本质
不存在变量提升
// var
console.log(a)
// 变量提升
var a = 5
// 相当于
var a
console.log(a)
a = 5
// let
console.log(a)
let a = 5
暂时性死区
var temp = 123
if (true) {
temp='abc'
let temp
}
虽然声明了全局变量tmp,但是块级作用域只要let命令,它声明的变量就会绑定到这个区域,所以tmp是声明之前赋值就会报错
不允许重复声明
// var允许重复定义,后定义的会覆盖掉先定义的
var a=5
var a=6
console.log(a)
// let 不允许重复定义
let a=5
let a=6
console.log(a)
块级作用域
问题1:内层变量可能覆盖外层变量
// var
function f1() {
var n = 5
if (true) {
var n = 10
}
console.log(n)
}
f1()
// let
function f1() {
let n = 5
if (true) {
let n = 10
}
console.log(n)
}
f1()
问题2:用来计数循环的变量泄露为全局变量
// var
for(var i=0; i<3; i++) {
//
}
console.log(i) // 3
// let
for(let i=0; i<3; i++) {
//
}
console.log(i) // 3
不属于顶层对象window
// var
var a = 5;
console.log(a)
console.log(window.a)
//let
let a = 5
console.log(a)
console.log(window.a)
let避免污染全局变量
const:一旦声明,必须立即初始化
const c
c = 9
const:本质
const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值)而言,值就保存在变量指向的内存地址中,因此等同于常量。但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是一个指针,const只能保证这个指针是固定的。
// 对象
const foo = {}
foo.prop = 123
// 添加属性,可以
console.log(foo)
// 指向另一个对象,报错
foo = {}
// 数组
const a = []
// 可以
a.push('bianyu')
console.log(a)
// 可以
a.length = 0
console.log(a)
// 报错
a = ['test']
若是想将对象冻结,用Object.freeze
const foo = Object.freeze({})
foo.props = 123
console.log(foo)
解构赋值
按照一定模式(两侧结构相同),从数组和对象中提取值,对变量进行赋值
数组结构
let arr = [1, 2, 3]
let a = arr[0]
let b = arr[1]
let c = arr[2]
// 等价的
let [a, b, c] = arr
console.log(a, b, c)
// 二维数组
let [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a, b, c, d)
let [a, b, [c]] = [1, 2, [3, 4]]
console.log(a, b, c)
let [a, b, c] = [1, 2, [3, 4]]
console.log(a, b, c)
// d为undefined,相当于定义了一个d,没有给它初始值
let [a, b, c, d] = [1, 2, [3, 4]]
console.log(a, b, c, d)
let [a, b, c, d=5] = [1, 2, [3, 4], 6]
console.log(a, b, c, d)
对象解构
let user = {
name: 'bian',
age: 18
}
let name = user.name
let age = user.age
// 等价
let {name, age} = user
console.log(name, age)
let user = {
name: 'bian',
age: 18
}
// 令起一个名字
let {age: uage, name: uname} = user
console.log(uage, uname)
字符串
let str = 'es6learn'
for (let i=0;i<str.length;i++) {
console.log(str[i])
}
// 理解为数组的解构
let [a, b, c, d, e, f, g, h] = str
console.log(a, b, c, d, e, f, g ,h)
数组的遍历方式
es5中
- for循环
- forEach(): 没有返回值,只针对每个元素调用函数
- map(): 返回新的数组,每个元素为调用函数的结果
- filter(): 返回符合函数条件的元素数组
- some(): 返回布尔值,判断是否有满足函数条件的元素
- ervey(): 返回布尔值,判断每个元素是否满足函数条件
- reduce(): 接受一个函数作为累加器
- for in
let arr = [1, 2, 3]
for(let i = 0; i< arr.length; i++) {
console.log(arr[i])
}
// 等同
arr.forEach((item) => {
console.log(item)
})
forEach无法跳出循环,即无法使用break和continue
let arr = [1, 2, 3]
let result = arr.map((item) => {
item += 1
return item
})
console.log(arr, result)
返回新数组,不改变原有数组
let arr = [1, 2, 3]
let result = arr.filter((item) => {
return item%2!==0
})
console.log(arr, result)
返回新数组,不改变原有数组
let arr = [1, 2, 3]
let result = arr.some((item) => {
return item%4===0
})
console.log(arr, result)
返回布尔值,不改变原数组
let arr = [1, 2, 3]
let result = arr.every((item) => {
return item%2===0
})
console.log(arr, result)
返回布尔值,不改变原数组
let arr = [1, 2,2,3, 3]
// 累加
let result = arr.reduce((prev, cur, index, arr)=> {
return prev + cur
}, 0,)
console.log(result)
let arr = [1,3,5,6,6]
Array.prototype.foo = ()=>{
console.log('foo')
}
for(let i in arr) {
console.log(arr[i])
}
for in 在遍历数组时存在问题,不可取
es6中
find
返回第一个通过测试的元素
let arr = [1,3,5,6,6]
let result = arr.find((item)=>{
return item === 6
})
console.log(arr, result)
findIndex
第一次出现元素的索引
let arr = [1,3,5,6,6]
let result = arr.findIndex((item)=>{
return item === 5
})
console.log(arr, result)
for of
let arr = [1,3,5,6,6]
Array.prototype.foo = ()=>{
console.log('foo')
}
for(let item of arr) {
console.log(item)
}
数组的扩展
类数组/伪数组
let divs = document.getElementsByTagName('div')
console.log(divs) // HTMLCollection
console.log(divs instanceof Array) //false
let classes = document.getElementsByClassName('div')
console.log(classes) // HTMLCollection
console.log(classes instanceof Array) //false
let query = document.querySelectorAll('div')
console.log(query) // NodeList
console.log(query instanceof Array) //false
query.push(123) // 报错
function foo () {
console.log(arguments)
console.log(arguments instanceof Array)
}
foo(2, 'test', true)
并不是真正意义上的数组,是一个含有length属性的json对象,它是按照索引的方式存储数据,它并不具有数组的一些方法。
Array.from
将类数组转化为数组
let arrLike = {
0: '0',
1: '1',
2: '2',
length: 3
}
let arr = Array.from(arrLike)
console.log(arr)
console.log(arr[1])
Array.of
将一组值转化为数组
let arr1 = new Array(1,2)
console.log(arr1)
let arr2 = new Array(3)
console.log(arr2)
传1个参数代表数组的长度,1个以上为数组的参数,行为不统一
let arr1 = Array.of(1,2)
console.log(arr1)
let arr2 = Array.of(3)
console.log(arr2)
// 拼装新数组
let arr = Array.of(1, true, 'test', [1,3], {name: 'bian '})
console.log(arr)
copyWithin
在当前数组内部将指定位置的成员复制到其他位置(会覆盖原有成员)
接收3个参数
- target(必选):从该位置开始替换
- start(可选):从该位置开始读取,默认0,负值表示倒数
- end(可选):到该位置前停止读取,默认为数组的长度,负值表示倒数
let arr = [1, 2, 3, 4, 5]
let arr1 = [1,2,3,4,5].copyWithin(0,3)
console.log(arr1)
let arr2 = [1,2,3,4,5].copyWithin(0,3,4)
console.log(arr2)
let arr3 = [1,2,3,4,5].copyWithin(0,-2,-1)
console.log(arr3)
fill
用给定值填充一个数组
3个参数,填充的数值,起始位置,结束位置
let arr1 = ['a','b','c'].fill(7)
console.log(arr1)
let arr2 = new Array(3).fill(7)
console.log(arr2)
let arr3 = ['a','b','c'].fill(7,1,2)
console.log(arr3)
includes
用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。
let arr = [1, 3, 3, NaN]
console.log(arr.indexOf(NaN))
console.log(arr.includes(NaN))
es5的indexOf不可判断
NAN
,includes可以
函数
参数的默认值
// 可传默认值
function foo(x, y='world') {
console.log(x, y)
}
foo ('hello')
// 默认声明,不可重复声明
function foo(x=5) {
let x = 1
}
foo()
function foo(x, y=5, z) {
console.log(x, y, z)
}
foo(1,4)
function foo(x, z, y=5) {
console.log(x, y, z) // 1 5 4
}
foo(1,4)
带默认值的参数放在所有参数的后面
与解构赋值的结合
function foo ({x,y=5}) {
console.log(x,y)
}
foo({})
foo({x: 1})
foo({x:1,y:3})
foo()
两侧的结构必须对应
length属性
function foo(x, y=1,z=3) {
console.log(x, y)
}
// 没有指定默认值的此参数的个数
console.log(foo.length)
作用域
// 参数一旦设置默认值,就会形成一个单独的作用域
let x = 1
function foo(x, y = x) {
console.log(y)
}
foo(2)
let x = 1
// 当前作用域找不到,向上级作用域查找
function foo(y = x) {
let x = 2
console.log(y)
}
foo()
rest参数
把用逗号分割的项组成一个数组
function foo (...args) {
console.log(args)
}
foo(1,2)
foo(1,2,3)
function foo (x, ...args) {
console.log(x, args)
}
foo(1, 2,3)
箭头函数
- this指向定义时所在的对象,而不是调用时所在的对象
- 不可以当构造函数
- 不可以使用arguments对象
// 左边是参数,右边是函数体
let sum = (x, y) => {
return x+y
}
// 只有一行代码,可以简写
let sum = (x, y) => x+y
console.log(sum(2,4))
let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function(){
setTimeout(function(){
console.log(this)
}, 1000)
})
this指向调用时的对象
let oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', function(){
setTimeout(() => {
console.log(this)
}, 1000)
})
箭头函数指向定义时的对象
let People = (name, age) => {
this.name = name
this.age = age
}
let p1 = new People('bian', 18)
console.log(p1)
不可以当构造函数
let foo = () => {
console.log(arguments)
}
foo(1, 3, 4)
不可以使用arguments对象
对象的扩展
属性简洁表示法
let name = 'bian'
let age = 18
let obj = {
name,
age
}
console.log(obj)
键值相等可简写
属性名的表达式
let name = 'bian'
let age = 18
let s = 'job'
let obj = {
name,
age,
[s]: 'font'
}
console.log(obj)
[]代表key值为变量
Object.is
判断两个的值是否严格相等
console.log(Object.is(2, '2'))
console.log(Object.is(NaN, NaN))
console.log(Object.is(+0, -0))
let obj1 = {
name: 'bian',
age: 18
}
let obj2 = {
name: 'bian',
age: 18
}
console.log(Object.is(obj1, obj2))
Object.assign()
用于合并对象,后面的会覆盖前面的
let x = {
a: 3,
b: 4
}
let y = {
c: 5,
a: 6
}
Object.assign(y, x)
console.log(y)
in
判断当对象中是否包含某个属性
let y = {
c: 5,
a: 6
}
console.log('a' in y, 'd' in y)
对象的遍历
let obj = {
name: 'bian',
age: 18,
job: 'font'
}
for(let key in obj) {
console.log(key, obj[key])
}
console.log('--')
Object.keys(obj).forEach(key => {
console.log(key, obj[key])
})
console.log('--')
Object.getOwnPropertyNames(obj).forEach(key => {
console.log(key, obj[key])
})
console.log('--')
Object.ownKeys(obj).forEach(key => {
console.log(key, obj[key])
})
深拷贝与浅拷贝
let target = {
a: {
b: {
c: 1
},
e: 4,
f: 5,
g: 6
}
}
let source = {
a: {
b: {
c: 1
},
e: 2,
f: 3
}
}
Object.assign(target, source)
console.log(target) // g丢失
用Object.assing去赋值一个比较复杂的结构时并不安全,只是浅拷贝
let obj1 = {
name: 'bian',
age: 18
}
let obj2 = obj1
obj1.age = 16
console.log(obj1, obj2)
相当于obj1和obj2指向了同一块内存地址,浅拷贝
let obj1 = {
name: 'bian',
age: 18
}
let obj2 = JSON.parse(JSON.stringify(obj1))
obj1.age=16
console.log(obj1, obj2)
JSON.parse(JSON.stringify())对象里的方法不能拷贝
类与继承
// 类
class People {
// 构造函数
constructor(name, age) {
this.name = name
this.age = age
}
showName() {
console.log(this.name)
}
}
let p1 = new People('bian', 18)
console.log(p1)
p1.showName()
// 继承
class Coder extends People {
constructor(name, age, company) {
// 继承当前父类的属性
super(name, age)
this.company = company
}
showCompany() {
console.log(this.company)
}
}
let c1 = new Coder('yu', 26, 'cmss')
console.log(c1)
c1.showName()
c1.showCompany()
// 类
class People {
// 构造函数
constructor(name, age) {
this.name = name
this.age = age
this._sex = 'female'
}
get sex() {
return this._sex
}
set sex(val) {
this._sex = val
}
}
let p1 = new People('bian', 18)
console.log(p1.sex)
p1.sex = 'male'
console.log(p1.sex)
在类的顶层定义属性的时候要用get和set,里面可以写一些业务逻辑
// 类
class People {
// 构造函数
constructor(name, age) {
this.name = name
this.age = age
}
// 静态方法,不能被实例调用,子类可以调用
static getCount() {
return 5
}
}
let p1 = new People('bian', 18)
console.log(People.getCount())
// class不支持静态属性的定义,可以定义在class外部
People.count = 9
console.log(People.count)
Symbol
新的原始数据类型(symbol、undefined、null、sring、boolean、number、object),代表唯一标识
let s1 = Symbol()
let s2 = Symbol()
console.log(s1)
console.log(s2)
console.log(s1===s2)
// for,相当于定义在全局的环境
let s1 = Symbol.for('foo')
console.log(s1)
let s2 = Symbol.for('foo')
console.log(s2)
console.log(s1 === s2)
不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key值是否已经存在,不存在的话才会新建一个值
let s1 = Symbol('foo')
console.log(Symbol.keyFor(s1))
// 返回已经登记的全局的
const s2 = Symbol.for('foo')
console.log(Symbol.keyFor(s2))
const grade = {
张三: {address: '1', tel: '2'},
李四: {address: '2', tel: '2'},
李四: {address: '3', tel: '3'}
}
console.log(grade)
对象中key必须是唯一的,后定义的会覆盖先定义的
const stu1 = Symbol('李四')
const stu2 = Symbol('李四')
const grade = {
张三: {address: '1', tel: '2'},
[stu1]: {address: '2', tel: '2'},
[stu2]: {address: '3', tel: '3'}
}
console.log(grade)
把Symbol作为当前对象的key,保证key不冲突
// const shapeType = {
// triangle: 'Triangle',
// circle: 'Circle'
// }
const shapeType = {
triangle: Symbol(),
circle: Symbol()
}
// 消除魔术字符串
function getArea(shape) {
let area = 0
switch(shape) {
case shapeType.triangle:
area = 1
break
case shapeType.circle:
area = 2
break
}
return area
}
console.log(getArea(shapeType.triangle))
Set
新的数据结构,其中不会有重复的值
let s = new Set([1, 2, 3, 3])
// 不可重复
console.log(s)
console.log('--')
// 添加可链式操作
s.add('1').add('2')
console.log(s)
console.log('--')
// 删除
s.delete(2)
console.log(s)
console.log('--')
// 判断是否有值
console.log(s.has('1'))
console.log(s.has(5))
console.log('--')
// set大小
console.log(s.size)
console.log('--')
// set循环遍历key和value是相等的
for(let i of s.entries()) {
console.log(i[0], i[1])
}
console.log('--')
// 清空
s.clear()
console.log(s)
// 数组去重
let arr = [1,3,3,4,5,5]
let s = new Set(arr)
console.log(s)
// 合并去重
let arr1 = [1,2,3,4]
let arr2 = [2,3,4,5,6]
let s = new Set([...arr1, ...arr2])
console.log([...s])
console.log(Array.from(s))
Map
键值对的形式,他的key不仅限于字符串
let m = new Map()
let obj = {
name: 'bian'
}
// 设置值
m.set(obj, 'test')
console.log(m)
console.log('--')
// 获取值
console.log(m.get(obj))
console.log('--')
// 是否有值
console.log(m.has(obj))
console.log('--')
// 大小
console.log(m.size)
console.log('--')
// 删除值
m.delete(obj)
console.log(m)
console.log('--')
字符串扩展
字符串的遍历器接口
for (let i of 'test') {
console.log(i)
}
模板字符串
// 字符串换行
const str = `test
test1
test2`
console.log(str)
// 逻辑运算拼接
const a = 20
const b = 14
const c = 'test'
const str = `${c}${a+b}`
console.log(str)
includes、startsWith、endsWith
const s = 'Hello World!'
console.log(s.startsWith('Hello'))
console.log(s.endsWith('!'))
console.log(s.includes('o'))
支持第二个参数,表示搜索的开始位置
repeat
字符串重复n次
console.log('x'.repeat(3))
console.log('hello'.repeat(2))
数值的扩展
二八进制
// es5
// 十 -> 二
const a = 5
console.log(a.toString(2))
// 二 -> 十
const b = 101
console.log(parseInt(b, 2))
// es6 0B二进制 0O八进制
const c = 0B0101
console.log(c)
const d = 0O777
console.log(d)
Number.isFinite()、Number.isNaN()
Number.isFinite()判断是否是有限的
Number.isNaN()判断是不是NaN
console.log(Number.isFinite(5))
console.log(Number.isFinite(Infinity))
// 非数字一律为false
console.log(Number.isFinite('test'))
console.log(Number.isFinite(true))
console.log(Number.isNaN(NaN))
console.log(Number.isNaN(15))
Number.parseInt()、Number.parseFloat()
console.log(Number.parseInt(5.5))
console.log(Number.parseFloat(5.5))
Number.isInteger()
判断是否为整数
console.log(Number.isInteger(5))
console.log(Number.isInteger(5.5))
Math.trunc()
去除小数部分
console.log(Math.trunc(5.5))
console.log(Math.trunc(-5.5))
Math.sign
判断正数、负数、0
console.log(Math.sign(5))
console.log(Math.sign(-5))
console.log(Math.sign(0))
console.log(Math.sign(NaN))
console.log(Math.sign(true))
console.log(Math.sign(false))
Math.cbrt
计算立方根
console.log(Math.cbrt(8))
console.log(Math.cbrt(9))
console.log(Math.cbrt('test'))
Proxy
Proxy可以理解成在目标对象前架设一个‘拦截’层,外界对该对象的访问都必须先通过这层拦截,一次提供了一种机制可以对外界的访问进行过滤和改写。
Proxy接受两个参数,第一个参数是所要代理的目标对象,第二个参数是一个配置对象。
get
用于拦截某个属性的读取操作,get方法的两个参数分别表示目标对象和要访问的属性
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop) {
return prop in target ? target[prop]: 'error'
}
})
console.log(arr[1])
console.log(arr[8])
set
用于拦截某个属性的赋值操作,set方法的三个参数分别表示目标对象和要访问的属性和要设置的值
let arr = []
arr = new Proxy(arr, {
set(target, prop, val) {
if (typeof val === 'number' || typeof val === 'string') {
target[prop] = val
return true
} else {
return false
}
}
})
arr.push(5)
arr.push('5')
console.log(arr[0],arr[1])
has
用来拦截HasProperty操作,has方法的两个参数分别表示目标对象和要访问的属性
let range = {
start: 1,
end: 5
}
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end
}
})
console.log(2 in range)
console.log(9 in range)
ownKeys
拦截自身属性的读取操作,循环遍历的时候常用
// 私有属性不被外部取到
let userinfo = {
username: 'bian',
age: '34',
_password: '***'
}
for(let key in userinfo) {
console.log(key)
}
console.log('---')
userinfo = new Proxy(userinfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
for(let key in userinfo) {
console.log(key)
}
deleteProperty
用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性无法被delete命令删除
let user = {
username: 'bian',
age: '18',
_password: '***'
}
user = new Proxy(user, {
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error('不可删除')
} else {
delete target[prop]
return true
}
}
})
delete user.age
console.log(user)
delete user._password
console.log(user)
apply
拦截函数的调用,接收3个参数,分别是目标对象、目标对象的上下文对象、目标对象的参数数组
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
apply(target, ctx, args) {
return target(...args) * 2
}
})
console.log(sum(1,2))
contruct
拦截new命令,接收三个参数目标对象,构建函数的参数对象,new命令作用的构造函数
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget) {
console.log('construct')
return new target(...args)
}
})
console.log(new User('test'))
Reflect
将Object属于语言内部的方法放到Reflect上
将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty)放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只放在Reflect对象上部署。
let obj = {}
let newVal = ''
// 等价
Reflect.defineProperty(obj, 'name', {
// Object.defineProperty(obj, 'name', {
get() {
return newVal
},
set(val) {
console.log('set')
newVal = val
}
})
obj.name = 'es'
console.log(obj.name)
修改某些Object方法的返回结果,让其便的合理
try {
Object.defineProperty()
} catch(e) {
}
if (Reflect.defineProperty()) { // boolean
} else {
}
让Object操作变成函数行为
console.log('assign' in Object) // 命令式操作
console.log(Reflect.has(Object, 'assign'))
Reflect对象的方法与Proxy对象的方法一一对应
let user = {
username: 'bian',
age: '18',
_password: '***'
}
user = new Proxy(user, {
get(target, prop) {
if(prop.startsWith('_')) {
throw new Error('不可访问')
} else {
// return target[prop]
return Reflect.get(target, prop)
}
},
set (target, prop, val) {
if (prop.startsWith('_')) {
throw new Error('不可访问')
} else {
// target[prop] = val
Reflect.set(target, prop, val)
return true
}
},
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error('不可删除')
} else {
Reflect.deleteProperty(target, prop)
// delete target[prop]
return true
}
}
})
Promise
// 基本用法
// resolve异步操作执行成功时的操作
// reject异步操作执行失败时的操作
let p = new Promise((resolve, reject) => {
setTimeout(()=> {
console.log('test')
resolve('成功')
// reject('失败')
}, 1000)
}).then((res) => {
console.log(res)
}, (res)=>{
console.log(res)
})
let p = new Promise((resolve, reject) => {
resolve(1)
// pending->fullfilled ,不能再变成rejected
reject(2)
})
p.then(res=>{
console.log(res)
}).catch(err => {
console.log(err)
})
三种状态,状态一旦改变就不可以再变,不可逆
// 解决回调地狱
function getPromise(url) {
return new Promise((resolve, reject)=>{
ajax(url, res => {
resolve(res)
})
})
}
// 链式调用
// 代码扁平化
getPromise('a')
.then(res => {
return getPromise('b')
}).then(res => {
return getPromise('c')
}).then(res => {
...
})
Promise.resolve、Promise.reject
将现有的对象转为Promise
function foo(flag) {
if(flag) {
return new Promise(resolve => {
resolve('success')
})
} else {
return Promise.reject('fail')
}
}
foo(false).then(res => {
console.log(res)
}, err=>{
console.log(err)
})
Promise.all、Promise.race
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve(1)
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2)
resolve(2)
}, 2000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3)
resolve(3)
}, 3000)
})
// 三个异步操作完成之后,再去做接下来的操作
Promise.all([p1, p2, p3]).then(res => {
// 三个都执行完,才执行这个
console.log(res)
}, err => {
console.log(err)
})
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve(1)
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2)
reject('2失败')
}, 2000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3)
resolve(3)
}, 3000)
})
// 三个异步操作完成之后,再去做接下来的操作
Promise.all([p1, p2, p3]).then(res => {
// 三个都执行完,才执行这个
console.log(res)
}, err => {
console.log(err)
})
all,只要有一个失败的,就认为全部失败
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve('1成功')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2)
resolve('2成功')
}, 2000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3)
resolve('3成功')
}, 3000)
})
Promise.race([p1, p2, p3]).then(res => {
console.log(res)
}, err => {
console.log(err)
})
只要有一个完成,就认为都完成
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
reject('1失败')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2)
resolve('2成功')
}, 2000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3)
resolve('3成功')
}, 3000)
})
Promise.race([p1, p2, p3]).then(res => {
console.log(res)
}, err => {
console.log(err)
})
有一个失败就认为整个都失败
Generator
function* foo() {
for(let i = 0; i< 3;i++) {
console.log(i)
yield i
}
}
let f = foo()
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
g需要手动执行,不会立即执行,生成一个迭代器,调用迭代器的next的函数是,会执行yield后面为止
Iterator
让不支持遍历的结构“可遍历”
let languages = {
allLanguage: {
font: ['vue', 'js', 'html', 'reace'],
back: ['java', 'python']
}
}
for (let i of languages) {
console.log(i)
}
// 可迭代协议,当前对象上要有Symbol.iterator属性
// 迭代器协议, 符合条件return {next(){return {value, done}}}
let languages = {
allLanguage: {
font: ['vue', 'js', 'html', 'react'],
back: ['java', 'python']
}
}
languages[Symbol.iterator] = function() {
let allLanguage = this.allLanguage
let keys = Reflect.ownKeys(allLanguage)
let values = []
return {
next() {
if (!values.length) {
if(keys.length) {
values = allLanguage[keys[0]]
keys.shift()
}
}
return {
done: !values.length,
value: values.shift()
}
}
}
}
for(let i of languages) {
console.log(i)
}
原生具备Iterator接口的数据结构
Array、
Map、
Set、
String、
TypedArray、
函数的arguments对象、
NodeList对象
Module
CommonJS: Node.js
仅仅局限于服务端
AMD:require.js
异步模块定义,在异步函数里面等待块加载完成,在回调函数里实现功能。依赖前置,提前执行
CMD:sea.js
依赖就近,延迟执行
ES6
export导入、import导入
// 一个文件中导出
const a = 5
const b = 'test'
const sum = (x, y) => {
return x + y
}
const obj = {
name: 'test'
}
class People {
constructor(name) {
this.name = name
}
showName() {
console.log(this.name)
}
}
export {a, b, sum, obj, People}
// 一个文件中导入
import {a, b, sum, obj, People} from './module'
console.log(a, b)
console.log(sum(1,2))
console.log(obj)
let p = new People('bian')
p.showName()
起别名 as
import {
a as aa,
b,
sum as he,
obj,
People
} from './module'
export default
// 在一个文件中导出
const a = 5
export default a
// 在另一个文件中导入
import test from './module'
console.log(test)
导出文件:一个文件中只能有一个export default,导入文件可以自己定义导入的模块名
export 和 export default 混用
// 从一个文件中导出
function sum (x, y) {
return x + y
}
export default sum
export const str = 'hahha'
// 在另一个文件中导入
import add, {str as haha} from './module'
console.log(add(1,2))
console.log(haha)
可以export defalut出文件中定义的所有值
// 从一个文件中导出
const a = 5
const b = 'test'
const sum = (x, y) => {
return x + y
}
const obj = {
name: 'test'
}
class People {
constructor(name) {
this.name = name
}
showName() {
console.log(this.name)
}
}
export default {
a, b, sum, obj, People
}
// 第一种导入方法
import Test from './module'
console.log(Test)
console.log(Test.sum(1,2))
// 第二种导入方法
import * as Test from './module'
console.log(Test)
console.log(Test.default.sum(1,2))
async / await
让异步操作顺序执行,async写在function的前面,await在async里面,他俩成对出现。代码简洁,减少嵌套。
function timeout() {
return new Promise(resolve => {
setTimeout(()=>{
console.log(1)
resolve('test')
}, 1000)
})
}
async function foo() {
// 相当于同步操作,要await后面的异步操作执行完毕才往下执行
const res = await timeout()
console.log(res)
console.log(2)
}
foo()
// reject的结果可以在catch中拿到
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
reject('test')
}, 1000)
})
}
async function foo() {
return await timeout()
}
foo().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})