ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。(题外话,现在都2020年了.....)
ES6 是 ECMAScript 标准十余年来变动最大的一个版本,为其添加了许多新的语法特性。
大部分浏览器都还是支持ES6新特性的。
运行ES6的环境:node.js或者使用webpack转码。
ES6新增特性
(一)新增块级变量 let 及常量 const
let、const和var的区别
- 作用域
let,const为局部变量,只可在代码块内访问。
var为全局变量,在全局范围内有效。
如下:
{
var a = 'var';
let b = 'let';
const c = 'const'
}
console.log(a)// var
console.log(b)// b is not defined
console.log(c)// c is not defined
let,var,const定义在函数内部时,都无法跨函数访问。如下:
function func(){
var a = 'var';
let b = 'let';
const c = 'const'
}
func();
console.log(a)// a is not defined
console.log(b)// b is not defined
console.log(c)// c is not defined
let,var,const定义在函数外部,函数内是可以访问的。如下:
var a = 'var';
let b = 'let';
const c = 'const'
function func(){
console.log(a)// var
console.log(b)// let
console.log(c)// const
}
func();
- 重复声明
var 可以重复声明,并且会覆盖之前的值。
let,const不可以重复声明,会报错。
如下:
var a = 'a'
var a = 'var'
console.log(a) // var
let b = 'b'
let b = 'let' // Identifier 'b' has already been declared
const c = 'c'
const c = 'const'// Identifier 'c' has already been declared
- 变量提升
var 存在变量提升,变量a已经声明了,没有赋值,打印出undefined
let,const 不存在变量提升,会报错。
如下:
console.log(a)// undefined
var a = 'var'
console.log(b)//b is not defined
let b = 'let'
console.log(c)//Cannot access 'c' before initialization
const c = 'const'
- 改变值
var,let定义后,可以改变值
const定义后,不可改变值
如下:
var a = 'a'
a = 'var'
console.log(a)// var
let b = 'b'
b = 'let'
console.log(b) //let
const c = 'c'
c = 'const' //Assignment to constant variable.
console.log(c)
值得注意的是,如果const定义的是一个对象,当改变对象内的属性值,是不会报错的。
如果const 声明的是复杂类型,只是相当于存储了一个指向内存的指针针地址,它只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。
const obj = {
a:'a',
b:0
}
// 给对象其中属性赋值,正常执行
obj.a = 'b'
console.log(obj)// { a:'b', b: 0}
// 如果是给对象赋值,就会报错
obj = {
a : '0'
}//Assignment to constant variable
- 初始化
const定义变量,必须初始化值,否则报错。
let,var可以不需要初始化。
如下:
var a;//正常执行
let b;//正常执行
const c;//Missing initializer in const declaration
- 暂时性死区
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
var p = 'var'
{
console.log(p)// Cannot access 'p' before initialization
p = 'new'//Cannot access 'p' before initialization
let p = 'let'
}
可以看到,p并没有去获取外面p的值,而是报错。
(二)函数的拓展
箭头函数
箭头函数是一种更简洁的函数书写形式。
参数 => 函数体
返回单行语句,return可以省略
let func = v => v+1
let func = (v) => v+1
// 等价于
let func2 = function(v){
return v+1
}
函数体有多行语句,使用{}
let func = (a,b) => {
a = a+b;
return a
}
返回对象,需要用()包裹起来
let func = () => ({
name:'yang'
})
箭头函数和普通函数有哪些区别?
- 没有this绑定
它的this,始终等于它上层上下文中的this
let funcEx = () => {
console.log(this)
}
let func = {
setFunc:function(){
console.log(this)
},
set:()=>{
console.log(this)
}
}
funcEx() // window
func.set() // window
func.setFunc()// func对象
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
var a = 20;
fn.call({a: 18}); // 18
- 没有arguments
let func = function fn(){
console.log(arguments)// ['age']
}
let func2 = () =>{
console.log(arguments)//arguments is not defined
}
func('age')
func2('name')
- 不能用作构造函数,没有new 方法
let fn = () => {
console.log('d')
}
let p = new fn() // fn is not a constructor
- 没有prototype原型
let fn = () => {
console.log('d')
}
fn.prototype.age = 10 //Cannot set property 'age' of undefined"
// 如果是普通函数
let fn2 = function (name) {
this.name = name
}
console.log(fn2.prototype) //{constructor: ƒ}
函数的默认参数
可以在传参时定义默认值。
function add(sum = '10'){
console.log(sum)
}
只有在参数为undefined的时候,才会使用默认值,null值是认为有效的。
function func(name,age=17){
console.log(name+","+age);
}
func("Amy",null); // Amy,null
函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值。
function func(x,y = x+1){
console.log(x,y)
}
func(1) // 1 2
function func2(x = y){
console.log(x)
}
func2() // y is not defined
不定参数
不确定参数的个数,可用展开运算符。
function func(...values){
console.log(values)
console.log(values.legnth)
}
func('name','age')// ["name", "age"] 2
func('name','age','sex')// ["name", "age", "sex"] 3
(三)字符串
字符串新增方法
- includes() 返回布尔值,判断一个对象是否包含一个指定的值。
includes 函数与 indexOf 函数很相似。但indexOf返回的是值的位置。
let str = 'i love you very'
str.includes('love') // true
- startsWith() 返回布尔值,判断字符串是否在头部。
let str = 'i love you very'
str.startsWith('i') // true
str.startsWith('love') // false
- endWith() 返回布尔值,判断字符串是否在尾部。
let str = 'i love you very'
str.endWith('very') // true
str.endWith('love') // false
- repeat() 返回重复指定次数新的字符串。
let str = 'love you'
console.log(str.repeat(2)) // love youlove you
// 小数向下取整
console.log(str.repeat(2.6)) // love youlove you
// 0 至 -1 之间的小数取整后为-0 等同于 0
console.log(str.repeat(-0.6)) // ""
// 小于-1的负数,报错
console.log(str.repeat(-6)) // Invalid count value
// NaN等同于0
console.log(str.repeat(2.6)) // ""
// 传入字符串,会先转为数字
console.log(str.repeat("3.4")) // "love youlove youlove you"
- padStart() 返回新字符串,将字符串补全到头部。
- padEnd() 返回新字符串,将字符串补全到尾部。
以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。
let str = 'you'
str.padStart(5,'love ') // "loyou"
str.padStart(10,'love ') // "love loyou"
str.padEnd(5,'love ') // ""youlo""
str.padEnd(10,'love ') // "youlove lo"
str.padEnd(10) // "you "
模板字符串
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
let data = 'app'
let str = 'this is a ${data}'
(四)数组
数组方法
- Array.of() 将参数中所有值作为元素形成数组。
console.log(Array.of(1,2,3,4)) // [1,2,3,4]
// 参数值可为不同类型
console.log(Array.of(1, '2', true)); // [1, '2', true]
// 参数为空时返回空数组
console.log(Array.of()); // []
- Array.from() 将类数组对象或可迭代对象转化为数组。
console.log(Array.from([1,2,3,4])) // [1,2,3,4]
// 参数值可为不同类型
console.log(Array.from([1,,3])); // [1, undefined, 3]
// 转换map
let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
console.log(Array.from(map)); // [['key0', 'value0'],['key1',
// 'value1']]
// 转换set
let arr = [1, 2, 3];
let set = new Set(arr);
console.log(Array.from(set)); // [1, 2, 3]
// 转换字符串
let str = 'abc';
console.log(Array.from(str)); // ["a", "b", "c"]
类数组对象
一个类数组对象必须含有 length属性,且元素属性名必须是数值或者可转换为数值的字符。
let arr = Array.from({
0: '1',
1: '2',
2: 3,
length: 3
});
console.log(); // ['1', '2', 3]
// 没有 length 属性,则返回空数组
let array = Array.from({
0: '1',
1: '2',
2: 3,
});
console.log(array); // []
// 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组
let array1 = Array.from({
a: 1,
b: 2,
length: 2
});
console.log(array1); // [undefined, undefined]
-
find() 查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
-
findIndex() 查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
-
fill(value,start,end)
将一定范围索引的数组元素内容填充为单个指定的值。
value:用来填充的值
start:被填充的起始索引
end:可选,被填充的结束索引,默认为数组末尾。
let arr = [1,2,3,4]
console.log(arr.fill()) //[undefined, undefined, undefined, undefined]
console.log(arr.fill(0,1)) // [1, 0, 0, 0]
console.log(arr.fill(0,1,2)) //[1, 0, 3, 4]
- entries() 遍历键值对,返回一个可迭代的键值对对象
let arr = ['name','age']
console.log(arr.entries())//Array Iterator{}
console.log(Array.from(arr.entries()))//[[0,'name'],[1,'age']]
let en = arr.entries()
console.log(en.next().value); // [0, "name"]
console.log(en.next().value); // [1, "age"]
for(let [key, value] of en){
console.log(key, value);
}
// 0 "name"
// 1 "age"
- keys() 返回一个包含数组键名的可迭代对象
let arr = ['name','age']
console.log(arr.keys())//Array Iterator{}
console.log(Array.from(arr.keys())) // [0,1]
- values() 返回一个包含数组键值的可迭代对象
let arr = ['name','age']
console.log(arr.values())//Array Iterator{}
console.log(Array.from(arr.values()))// ['name','age']
- includes() 判断数组中是否包含某个值
let arr = [1,2,3,4]
console.log(arr.includes(1)) // true
- flat(n) 扁平化数组
参数n代表嵌套数组扁平化的层数,默认为1
let arr = [1 ,[2, [3,[4,5]]]]
console.log(arr.flat()); // [1, 2, [3,[4,5]]]
console.log(arr.flat(2)); // [1, 2, 3,[4,5]]
// 不管嵌套多少层
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5]
// 自动跳过空位
console.log([1, [2, , 3]].flat());// [1, 2, 3]
- flatMap() 先对数组中每个元素进行处理,再对数组执行 flat() 方法
let arr = [1,2,3]
let res = arr.flatMap((cur,index,arr)=>{
return [cur *2]
})
console.log(res); // [2, 4, 6]
- 扩展运算符
可以复制数组。
let arr = [1,2,3]
let arr2 = [...arr]
console.log(arr2) // [1,2,3]
// 若数组有空位
let arr = [1,,3]
let arr2 = [...arr]
console.log(arr2) // [1,undefined,3]
值得注意的是,这种复制方法,只是一种浅拷贝,如果数组的值是一个引用类型,那么这个引用类型的属性值改变,将会影响其他复制的值。
let arr = [1,{name:'yang'}]
let arr2 = [...arr]
// 原始数据类型不会影响
arr2[0] = 2
console.log(arr) // [1,{name:'yang'}]
console.log(arr2) //[2,{name:'yang'}]
// 引用数据类型受到影响
arr2[1].name = 'arr2'
console.log(arr) // [1,{name:'arr2'}]
console.log(arr2) //[2,{name:'arr2'}]
合并数组:
let arr1 = [1,2]
let arr2 = [3,4]
let arr3 = [...arr1,...arr2] // [1,2,3,4]
// 同样的,如果arr1,arr2中的值有引用类型,当其改变时,会影响arr3。
(五)解构赋值
解构赋值是对赋值运算符的扩展。
允许按照一定模式,从数组和对象中提取值,对变量进行赋值
它可以分为两个部分:
源头:右边部分
目标:左边部分。
数组解构
- 基本
// 左右对称
let [a,b,c] = [1,2,3]
//a 1
//b 2
//c 3
// 左边缺省
let [a,,c] = [1,2,3]
//a 1
//c 3
// 右边缺省
let [a,b,c] = [1,2]
//a 1
//b 2
//c undefined
- 解构默认值
当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。
let [a=1,b] = [,3]
//a 1
//b 3
let [ a = 3, b = a] = []
// a 3
// b 3
let [ a = 3, b = a] = [1]
// a 1
// b 1
let [ a = 3, b = a] = [1,2]
// a 1
// b 2
- 嵌套数组
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
- 展开运算符
// 值得注意的是,展开运算符必须用在最后一个元素上
let [a, b, ...c] = [1,2,3,4,5,6];
// a = 1
// b = 2
// c = [3,4,5,6]
let [a, ...b, c] = [1,2,3,4,5,6]; //Rest element must be last element
对象解构
- 基本
// 左右对称 这里需要注意的是,解构的是值,而不是字段,所以nameN是不存在的
let { nameN:name, ageN:age } = { name:'yang', age:20}
// name yang
// age 20
// nameN is not defined
// 左右字段,必须一致
let { name, age } = { name2:'yang', age2:20}
// name undefined
// age undefined
// 左右对称 省略字段
let {name,age} = { name:'yang', age:20}
// name yang
// age 20
// 左边缺省
let {name,age} = { name:'yang', age:20 ,sex:'man'}
// name yang
// age 20
// 右边缺省
let {name,age} = { name:'yang'}
// name yang
// age undefined
- 默认值
let { name = 'yang', age = 10 } = { name: 'li' }
// name li
// age 10
let { nameN : name = 'mei' , ageN : age = 15 } = {name : 'gege'}
// name gege
// age 15
- 展开运算符
let { name,age,...other} = { name: 'li',age:10,sex:'man',weight:50 }
// name li
// age 10
// other { sex:'man',weight:50}
(六)symbol
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
(七)Map
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
(八)Set 对象
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
数组去重
let arr = [1,2,3,3,4,5,6,7,56,4]
console.log([...(new Set(arr))])
(九)Iterator
Symbol.iterator迭代器
Iterator 是 ES6 引入的一种新的遍历机制
-
迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
-
迭代器是用于遍历数据结构元素的指针
const items = ["zero", "one"];
const it = items[Symbol.iterator]();
it.next();
// {value: "zero", done: false}
it.next();
// {value: "one", done: false}
it.next();
// {value: undefined, done: true}
for...of循环
for...of 是 ES6 新引入的循环,用于替代 for..in 和 forEach() ,并且支持新的迭代协议。它可用于迭代常规的数据类型,如 Array 、 String 、 Map 和 Set 等等。
(十)async/await
同promise类似的异步操作。
(十一)promise
异步编程解决方案。
Promise是一个构造函数,通过new关键字创建实例。
(十二)模块import/export
在 ES6 前, 实现模块化使用的是 RequireJS 或者 seaJS(分别是基于 AMD 规范的模块化库, 和基于 CMD 规范的模块化库)。
ES6 的模块化分为导出(export) @与导入(import)两个模块。
(十三)类class
ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。
总结
参考文章:菜鸟教程 - ES6教程