文章目录
ES6
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现
ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准
需要配置 ES6 环境(Babel,Polyfill)
有的浏览器不支持 ES6,在运行前可以转换回 ES5 版本
编写过程 -> 编译过程 -> 运行过程
let 关键字
ES5:全局作用域、函数作用域
ES6:使用关键字 let 定义块级作用域变量
let 不存在声明提前,不允许重复声明,全局作用域中的变量与当前函数作用域无关,函数的参数相当于局部变量
块级作用域
块级作用域只能使用 let 关键字
let 关键字不仅可以声明块级作用域,还可以用在全局作用域和函数作用域
在块级作用域中声明函数,在全局作用域中可以正常访问或调用,但在声明之前访问或调用则会报错
变量的解构赋值
ECMAScript 6 允许按照一定模式从数组或对象中提取值,对变量进行赋值。这种赋值方式被称为“解构赋值”
如果解构赋值失败,变量的值等于 undefined。定义变量的数量大于值的数量
不完全解构赋值。定义变量的数量小于值的数量
解构赋值允许指定默认值,解构赋值失败时才有效。ECMAScript 6 内部使用全等于运算符判断指定位置是否存在值。所以只有当数组中成员的值等于 undefined 时,默认值才会生效
对象的解构赋值是通过变量名称与对象的属性名称一一对应实现的
数组的解构赋值,变量的索引值与值的索引值保持一致
// 对象解构赋值
let { m: { name, age }, n } = { m: { name: 'ww', age: 18 }, n: 10 }
console.log(name, age)
// 字符串解构赋值
let [a, b, c] = 'xyz'
console.log(a, b, c)
只要赋值运算符右边的值不是对象或数组的话,会先转换为对象。由于 undefined 和 null 无法转换为对象,所以解构赋值时会报错
作为值的类型必须是可迭代遍历的
只要有可能就不要在模式中使用小括号
用途:
- 交换变量的值
- 从函数返回多个值
- 提取 JSON 数据
字符串
es5
indexOf() 返回指定字符串中包含指定子字符串的第一个匹配的索引值
lastIndexOf() 返回指定字符串中包含指定子字符串的最后一个匹配的索引值
es6
includes() 判断一个字符串是否包含在另一个字符串中,返回布尔值,区分大小写
startsWith() 判断当前字符串是否是以另外一个给定的子字符串“开头”的,返回布尔值,区分大小写,表示指定字符串的指定索引值开始是否以另一个字符串开始的
endsWith() 判断当前字符串是否是以另外一个给定的子字符串”结尾“的,返回布尔值
repeat() 将原字符串重复 n 次,返回一个新字符串,不会改变原有字符串
如果参数为小数,则向下取整
如果参数为负数或者无穷大,则会报错
如果参数为 NaN,则为 0;如果参数为 0,没有任何输出
如果参数为 字符串,则会先转换为数字值
let num = new Number(100)
console.log(typeof num.toString())// string
let { toString: m } = 100
console.log(m === Number.prototype.toString)// true
模板字符串
模板字符串是增强版的字符串,使用反引号(``)来代替普通字符串中的用双引号和单引号
它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
模板字符串的功能可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能
标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数
原始字符串,应用在带标签的模板字符串,在函数的第一个参数中,存在着 raw 属性,获取到模板字符串的原始字符串
使用 String.raw() 方法创建原始字符串和使用默认模板函数和字符串连接创建是一样的
// 模板字符串
let str1 = 'this is string'
let str2 = `this is string`
let str3 = 'this'
+ ' \nis'
+ ' string'
let str4 = `this
is
string`
let username = 'ww'
let str5 = 'hello ' + username + ' !'
let str6 = `hello ${username} !`
数组
扩展运算符(…)允许一个表达式在原地展开,当需要多个参数(比如函数调用时)或者多个值(比如字面量数值),扩展运算符主要用于函数调用的参数(形参与实参)。
复制数组(深复制),合并数组,与解构赋值结合(将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错),字符串转换为数组(如果是关联数组,不会报错,但没有结果)
let arr = [1, 2, 3, 4, 5]
console.log(...arr)// 扩展运算符
call() 与 apply() 方法的区别
call(this, arg1, arg2, arg3, …) 接收参数为多个参数
apply(this, arr) 接收参数为一个数组
数组的深复制(复制数组中的元素内容(数据))与浅复制(复制数组的内存地址)
Array.from() 方法用于从一个类似数组或可迭代对象中创建一个新的数组实例,扩展运算符(…)也可以将某些数据结构转为数组
Array.of() 方法用于创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型;如果没有参数,则返回一个空数组;主要目的是弥补数组构造函数 Array() 的不足,因为参数个数的不同,会导致 Array() 的行为有差异
copyWithin() 方法用于浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小
检索指定数组中从 start 到 end 区间的元素,复制到当前数组中的指定索引值,不能改变数组的长度,修改了原有数组
arr.copyWithin(target, start, end)
target:0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算
start:0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算
end:0 为基底的索引,开始复制元素的结束位置。copyWithin() 将会拷贝到该位置,但不包括 end 这个位置的元素。
如果是负数,end 将从末尾开始计算
如果当前参数不写,自动截取到当前数组的最后
find() 方法返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined
findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引值,否则返回 undefined
数组调用 find() 方法,将指定数组进行遍历
callback 参数,调用 find() 方法时的回调函数
function(element,index,array){}
element 指定数组中每一项元素的值
index 指定数组中每一项元素的索引值
array 指定数组本身
fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引值
includes() 方法用于判断一个数组是否包含一个指定的值。如果包含返回 true,否则返回 false
let arr = [1, 2, 3, 4, 5]
console.log(arr)
let result = arr.fill(6, 1, 3)
console.log(result)
let result = [ 1, 6, 6, 4, 5 ]
console.log(result.includes(6, 1))
函数
ES5 中不允许为函数的形参设置其默认值,当定义形参,而不传递实参时,形参的默认值为 undefined
可以人为地解决形参默认值的问题,arg = arg || 0
当局部变量与形参同名时,局部变量会覆盖形参
如果为函数的参数设置默认值的话,当函数声明进行初始化时,参数会形成一个独立的作用域。这个作用域会在函数初始化完毕时消失
rest 参数
ECMAScript 6 新增了 rest 参数(…变量名),用于获取函数多余的参数,替代 arguments 对象。
与 rest 参数配合的变量是一个数组,该变量将多余的参数放入数组中。
rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错;函数的 length 属性,不包含 rest 函数
// es5 利用 arguments 对象接收多余的参数
function fn(a, b) {
console.log(a, b, arguments[2])
}
fn(1, 2, 3, 4, 5, 6, 7)
// es6 利用 rest 参数接收多余的参数(数组类型)
function fun(a, b, ...args) {
console.log(a, b, args)
}
fun(1, 2, 3, 4, 5)
箭头函数
ECMAScript 6 新增了箭头函数(又被称为胖箭头函数),相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名的。有两个因素会影响引入箭头函数:更简洁的函数和 this
// es5 定义函数
function fn() {
}
var fun = function () {
}
var f = new Function()
// es6 箭头函数
let n = () => 5
ES5 定义函数时,使用的 this 指向调用函数时的上下文对象
ES6 声明箭头函数,函数中的 this 指向的是定义箭头函数时的上下文对象
注意事项:
- 函数体内的 this,就是定义时所在的对象,而不是使用时所在的对象
- 不可以当作构造函数,也就是说不可以使用 new 命令,否则会抛出一个错误
- 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替
尾调用是函数式编程的一个重要概念。本身非常简单,就是指某个函数的最后一步是调用另一个函数,尾调用不一定出现在函数尾部,只要是最后一步操作即可
对象
ECMAScript 6 允许直接将变量和函数作为对象的属性和方法
Object.is() 方法,同值相等算法
// es5
console.log(+0 === -0)// true
console.log(NaN === NaN)// false
// es6
console.log(Object.is(+0, -0))// false
console.log(Object.is(NaN, NaN))// true
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,只会拷贝源对象自身的并且可枚举的属性到目标对象
var obj = { name: 'jj', age: 20 }
var target = {}
var temp = Object.assign(target, obj)
console.log(target)
console.log(temp)
ECMAScript 6 新增了 super 关键字用于指向当前对象的原型对象,super 关键字表示原型对象时,只能用在对象的方法中,用在其他地方都会报错
原型:函数中的一个特殊属性
const proto = { foo: 'hello' }
const ob = { foo: 'world', find() { return super.foo } }
// 将 proto 对象作为 ob 对象的原型对象
Object.setPrototypeOf(ob, proto)
console.log(ob.find())//hello
对象的扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象中
let z = { a: 3, b: 4 }
let x = { ...z }
console.log(x)
let s = Object.assign({}, z)
console.log(s)
键值对集合
Set 对象
Set 对象是值的集合,可以按照插入的顺序迭代它的元素。Set 集合中的元素只会出现一次,即 Set 集合中的元素是唯一的
NaN 和 undefined 都可以被存储在 Set 集合中,NaN 之间被视为相同的值
对象被存储在 Set 集合中时,两个对象总是不相等的。
可以利用 Set 集合为数组元素去重
// Set 集合
let set1 = new Set([1, 2, 3, 4, 5])
let set2 = new Set()
set2.add(1)
let set3=new Set().add(2)
// 遍历
let iterator = set1.values()
for (var item of iterator) {
console.log(item)
}
set1.forEach(function (value, key, set1) {
console.log(value, key, set1)
})
Set 集合与 Array 对比
- 数组中元素是否存在的 indexOf() 函数效率低下
- Set 对象允许根据值删除元素,而数组中必须使用基于下标的 splice() 方法
- 数组的 indexOf() 方法无法找到 NaN 值
- Set 对象存储不重复的值,所以不需要手动处理包含重复值的情况
WeakSet 对象
WeakSet 对象是一些对象值的集合,并且其中的每个对象值都只能出现一次
WeakSet 对象与 Set 对象的区别主要有两点:WeakSet 对象中只能存放对象引用,不能存放值。而 Set 对象都可以;WeakSet 对象中存储的对象值都是被弱引用的。如果没有其他的变量或属性引用这个对象值,则这个对象值会被当成垃圾回收掉,正因为这样,WeskSet 对象是无法被枚举的,没有办法拿到 WeakSet 集合包含的所有元素
Map 集合
Map 集合是键值对的集合。任何值都可以作为 Map 集合中的键或值。Map 集合可以按照插入的顺序迭代它的原地
let map = new Map()
let num = 100, st = 'aa', fu = function () { }, oj = {}
map.set('num', num)
map.set('st', st)
map.set('fu', fu)
map.set('oj', oj)
console.log(map)
Map 集合键的相等
- 判断使用与 === 相似的规则
- -0 和 +0 相等
- NaN 与自身相等(与 === 有所不同)
Map 集合与 Object 对比
一般情况下,Object 会被用于将字符串类型映射到数值。Object 允许设置键值对、根据键获取值、删除键、检测某个键是否存在。
而 Map 具有更多的优势:Object 的键均为 String 类型,在 Map 里键可以是任意类型;必须手动计算 Object 的尺寸,但是可以很容易地获取使用 Map 的尺寸;Map 的遍历遵循元素的插入顺序;Object 有原型,所以映射中有一些缺省的键
WeakMap 对象
WeakMap 对象也是键值对的集合。它的键必须是对象类型,值可以是任意类型。它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,他会被回收掉。WeakMap 提供的接口与 Map 相同
与 Map 对象不同的是,WeakMap 的键是不可枚举的。不提供列出其键的方法。列表是否存在取决于垃圾回收器的状态,是不可预知的
Promise 对象
ECMAScript 6 新增了 Promise 对象,该对象允许对延迟和异步操作流程进行控制。一个 Promise 对象就是一个代表了异步操作最终完成或者失败的对象
开发人员可以使用由其他函数创建并返回的 Promise 对象。Promise 对象本质上就是一个绑定了回调的对象,而不是将回调传进函数内部
一个 Promise 对象有一下几种状态:pending(初始状态,既不是成功也不是失败状态);fulfilled(意味着操作成功完成);rejected(意味着操作失败)
// Promise 的初始化状态,既不成功,也不失败
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('foo')
}, 300);
})
// 将 Promise 状态改变,要么成功,要么失败
promise.then((value) => {
console.log(value)
})
- 在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用
- 通过 .then 形式添加的回调函数,甚至都在异步操作完成之后才被添加的函数,都会被调用
- 通过多次 .then ,可以添加多个回调函数,它们会按照插入顺序并且独立运行
Promise 最直接的好处就是链式调用
Promise 对象原型的方法
then() 方法用于为 Promise 对象添加状态改变时回调函数。then() 方法的第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数
第一个回调函数:当 Promise 变成接受状态(fulfillment)时,该参数作为回调函数被调用。该函数有一个参数,即接收的最终结果
第二个回调函数:当 Promise 变成拒绝状态(rejection)时,该参数作为回调函数被调用。该函数有一个参数,即拒绝的原因
catch() 方法返回一个 Promise 对象,并且处理拒绝的情况。它的行为与调用 then() 方法相同
回调函数:当 Promise 被 rejected 时,被调用该回调函数。该函数具有一个参数,即 rejection 的原因
finally() 方法返回一个Promise 对象,在执行 then() 和 catch() 后,都会执行 finally 指定的回调函数。避免同样的语句需要在 then() 和 catch() 中各写一次的情况
回调函数:Promise 状态改变后的回调函数
finally() 方法是 ECMAScript 2018 引入标准的
Promise 对象的方法
Promise.resolve() 方法返回一个以给定值解析后的 Promise 对象。但如果这个值是个 thenable(即带有 then() 方法),返回的 Promise 会“跟随”这个 thenable 的对象。采用它的最终状态(指 resolved/rejected/pending/settled);如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve() 方法的返回值返回;否则以该值为成功状态返回 Promise 对象
value:将被 Promise 对象解析的参数。也可以是一个 Promise 对象,或者是一个 thenable(即带有 then() 方法)
Promise.reject() 方法返回一个带有拒绝原因 reason 参数的 Promise 对象
reason:表示 Promise 被拒绝的原因
Promise.all() 方法用于将多个 Promise 对象,包装成一个新的 Promise 对象
iterable:一个可迭代对象,如 Array 或 String 等
Promise.race() 方法返回一个 Promise 对象,一旦迭代器中的某个 Promise 解决或拒绝,返回的 Promise 就会解决或拒绝
iterable:一个可迭代对象,如 Array 或 String 等
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('one')
}, 100);
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('two')
}, 300);
})
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('three')
}, 200);
})
// Promise.all() 方法将多个 Promise 对象整合成一个 Promise 对象,是以所有 Promise 对象的状态都改变时,表示整合后的 Promise 对象成功或失败
let promise4 = Promise.all([promise1, promise2, promise3])
promise4.then((value) => {
console.log(value)
})
// 是以某个 Promise 对象的状态改变时,表示整合后的 Promise 对象成功或失败
Promise.race([promise1, promise2]).then((value) => {
console.log(value)
})
回调函数中的致命问题——回调陷阱(地狱)
async 函数
ECMAScript 2017 规范引入了 async 函数,该函数的主要目的就是简化使用 Promise 异步调用的操作,并对一组 Promise 执行某些操作。
正如 Promise 类似于结构化回调,async/await 类似于组合生成器和 Promises
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved')
}, 2000);
})
}
async function asyncCall() {
console.log('calling')
var result = await resolveAfter2Seconds()
console.log(result)
}
asyncCall()
异步函数声明式
异步函数表达式
async 函数返回一个 Promise 对象。async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数
// async 函数返回 Promise 对象
async function myAsync() {
return 'ee'
}
let pro = myAsync()
// console.log(pro)
pro.then((value) => {
console.log(value)
})
上述代码中,return 语句中没有 await 表达式,因为异步函数的返回值将被隐式地传递给 Promise.resolve
await 表达式
await 表达式用于等待一个 Promise 对象,它只能在异步函数中使用
错误处理:如果 await 后面的异步操作出错,那么等同于 async 函数返回 的 Promise 对象被 reject
为了防止错误,将其放置在 try…catch 语句中
多个 await 表达式后面的异步操作,如果不存在继发关系,最好让它们同时触发
await 表达式只能用在 async 函数之中,如果用在普通函数,就会报错
迭代器与生成器
ECMAScript 6 新增了第六种原始类型 Symbol(符号)类型。Symbol 类型是唯一的并且是不可修改的,并且也可以用来作为 Object 的 key 值
Symbol 类型的变量是通过调用 Symbol() 函数生成的
由于 Symbol 类型是原始类型,是不能通过 new Symbol() 来创建对象的
JavaScript 五种原始类型:number(Number)、string(String)、boolean(Boolean)、undefined(Undefined)、null(Null)
- 尝试将 symbol 值转换为一个 number 值时,会抛出一个 TypeError 错误
- Object(symbol) == symbol 表达式的结果返回true
- 阻止从一个 symbol 值隐式地创建一个新的 string 类型的属性名
Symbol.for() 方法,根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol。如果找到了,则返回它。否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中
Symbol.keyFor() 方法用于获取 symbol 注册表中某个 symbol 关联的键
Symbol 与 for…in
Smbols 在 for…in 迭代中不可枚举。另外,Object.getOwnPropertyNames() 不会返回 symbol 对象的属性,但是可以使用 Object.getOwnPropertySymbols() 得到它们
迭代器
ECMAScript 5 规范表示集合的数据结构有数组(Array)和对象(Object),ECMAScript 6 规范又新增了 Set 和 Map 两种集合
在 JavaScript 中就有四种集合,需要一种统一的机制进行操作
迭代器就是这样的一种机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 lterator 接口,就可以完成遍历操作
迭代器具有三种作用:
- 为各种数据结构,提供一个统一的简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ECMAscript 6 新增了 for…of 循环语句,用于遍历迭代器
next() 方法用于返回序列中的下一项,该方法返回包含 done 和 value 两个属性的对象
迭代器对象一旦被创建,就可以反复调用 next() 方法用于依次访问对象中的键值
function f(array) {
var index = 0
return {
next: function () {
return index < array.length ? {
done: false,
value: array[index++]
} : {
done: true
}
}
}
}
一种数据结构只要部署了 lterator 接口,就可以称这种数据结构是“可遍历的”
一个数据结构只要具有 Symbol.iterator 属性,就可以认为是“可遍历的”
Symbol.iterator 属性本身就是一个函数,就是当前数据结构默认的迭代器生成函数。执行这个函数,就会返回一个迭代器
可迭代协议
迭代器协议
for…of 语句
ECMAScript 6 引入了 for…of 语句用于遍历迭代器
一个数据结构只要部署了 Symbol.iterator 属性,就被称为具有 lterator 接口,就可以用 for…of 循环遍历它的成员
和 forEach() 方法的区别
forEach() 方法无法跳出循环。break 语句和 continue 语句无效
for…of 语句不仅可以使用 break 语句和 continue 语句,还可以配合使用 return 语句
与 for…in 语句的区别,主要用于遍历对象
for…in 语句不仅遍历自身,还会遍历手动添加的,甚至包括原型链的
如果用于遍历数组的话,遍历得到的键名为字符串类型的数字值
for (let attr of arr) {
console.log(attr)
}
返回迭代器对象的方法
数组(Array)、Set 和 Map
entries() 方法:返回一个新的迭代器对象,这个对象的元素是用来遍历 [键名, 键值] 组成的数组
keys() 方法:返回一个新的迭代器对象,用来遍历所有的键名
values() 方法:返回一个新的迭代器对象,用来遍历所有的键值
生成器
Generator 函数可以作为生成一个迭代器的特殊函数,该函数被调用时返回一个 Generator 对象,该对象是符合可迭代协议和迭代器协议的
function* 这种声明方式会定义一个生成器函数,它返回一个 Generator 对象
yield 关键字用来暂停和恢复一个生成器函数
yield* 表达式用于委托给另一个 Generator 或可迭代对象
Class
ECMAScript 6 提供了更接近传统开发语言的写法,引入了类(Class)的概念。
类作为对象的模板,只是一个语法糖
class 关键字只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
类的声明方式
类的表达式方式
不允许声明提前
不允许重复声明
// es5
function Hero() {
this.name = 'kk'
this.sayMe = function () {
console.log('ww')
}
}
Hero.prototype = { age: 20 }
let hero = new Hero()
// es6
class Point {
// 构造器
constructor() {
this.name = 'ee'
this.meSay = function () {
console.log('aa')
}
}
}
构造函数(Constructor)是用来创建和初始化类中创建的一个对象的一种特殊方法
在一个类中只能有一个名为“constructor”的特殊方法
在一个构造方法中可以使用 super 关键字来调用一个父类的构造方法
如果不指定一个构造函数方法,则使用一个默认的构造函数
静态方法
static 关键字为一个类定义了一个静态方法
静态方法不会在类的实例上被调用,相反被类本身调用
从另一个静态方法调用
从类的构造函数和其他方法调用,静态方法不能直接在非静态方法中使用 this 关键字来访问
类的继承
extends 关键字用于类声明或者类表达式中,以创建一个类,该类作为另一个类的子类
用来创建一个普通类或者内建对象的子类
继承的 .prototype 必须是一个 Object 或者 null
super 指向当前子类的父类的构造器
super 关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同
super 关键字作为函数调用时,代表父类的构造函数。ECMAScript 6 要求子类的构造函数不许执行一次 super 函数
super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类