ES6新特性速览
ES6新特性速览
虽然
ES6
现在基本都是前端必会内容了,网上也一堆文章了,但有些初学者可能看完还是记不住,所以便写一篇文章快速过一下ES6
的一些新特性,加深印象。如果你还没有学ES6
,还是推荐你阅读一下阮一峰的ES6入门指南。
☀️如果觉得文章不错,欢迎点赞❤️、关注🌟、收藏📄!
🚀ES6
的新特性,大致可以归为4大类:
- 解决原有语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象、全新的方法、全新的功能
- 全新的数据类型和数据结构(symbol、set、map、etc.)
块级作用域
作用域——某个成员能够起作用的范围,在ES2015之前,只有两种作用域:
全局作用域
和函数作用域
。在ES2015中新增:块级作用域
.
//var 会变量提升,所以值会是undefined
console.log(a);//undefined
var a=1;
//let 不会进行变量提升
console.log(b)//Cannot access 'b' before initialization
let b=2;
//块级作用域
{
let c=20;
}
console.log(c) //c is not defined
//const 申明时候必须赋值,且声明之后不可修改值,但可以修改对象中的某个值,
//const 和let一样,也存在块级作用域
const a={name:'lonjin'};
a.name='tom';
console.log(a)//{ name: 'tom' }
- 经典面试题:
for(var i=1;i<6;i++){
setTimeout(() => {
console.log(i)
}, 0);
}
// 6 6 6 6 6
for(let i=1;i<6;i++){
setTimeout(() => {
console.log(i)
}, 0);
}
// 1 2 3 4 5
数组的解构
- 基本使用
const arr=[1,2,3,4,5];
const [a, b, c] = arr;
console.log(a,b,c) //1 2 3
- 只获取数组中第三个成员,方法,前面两个用逗号隔开,只声明第三个
const arr=[1,2,3];
const [,,c]=arr;
console.log(c) //3
- 提取数组当前位置开始的所有成员(注意:三个点的用法只能在解构成员的最后使用)
const arr=[1,2,3,4,5];
const [a,...arr2]=arr;
console.log(arr2) //[ 2, 3, 4, 5 ]
- 可以给提取到的成员设置默认值,如果没有提取到值,就会赋为默认值
const arr=[1,2,3,4,5];
const [a,b,c,d,e,f=6]=arr;
console.log(f) //6
对象的解构
- 基本使用(和数组解构基本相同)
const data={name:"lonjin",age:18};
const {name}=data;
console.log(name) //lonjin
- 如果解构过程中遇到命名重复,可使用重命名的方式解决;设置默认值方法如下:
const data={name:"lonjin",age:18};
const name='tom';
const {name:newName}=data;
console.log(newName) //lonjin
- 同样也可以设置默认值
const data={name:"lonjin",age:18};
const name='tom';
const {name:newName,say='hello'}=data;
console.log(newName+','+say) //lonjin,hello
- 解构常用方法,如
console.log
const {log}=console;
log(1)//1
模版字符串
-
反引号定义字符串,若字符串中需要有反引号,可以用\反斜杠转义
-
支持字符串中直接换行
-
支持插值表达式写法 `hello, ${name}
const name='lonjin';
const text=`hello ${name} \nuser age ${17+1}`;
console.log(text)
/*
hello lonjin
user age 18
*/
带标签的模板字符串
- 标签模板字符串的作用:是对我们的模板字符串进行加工处理,然后返回新内容
const name='lonjin',
sex=true,
age=18;
function fn(strings,...args){
//string为text中普通string数组
console.log(strings)
//[ 'my name is ', ',my age', ',my sex', '' ]
//args为插值表达式中的值
console.log(args)
// [ 'lonjin', 18, true ]
//可以处理完数据return出去
let sex=args[2]?'man':woman;
return strings[0]+args[0]+strings[1]+args[1]+strings[2]+sex
}
const text=fn`my name is ${name}my age${age}my sex ${sex}`;
console.log(text)
//my name is lonjinmy age18my sex man
字符串的扩展方法
includes()
:判断一个字符串是否包含在另一个字符串中,返回true
或者false
const str='erro type end.';
let f1=str.includes('erro');
console.log(f1)//true
startsWith()
:判断当前字符串是否以另外一个给定的子字符串开头,返回true
或者false
const str='erro type end.';
let f2=str.startsWith('erro');
console.log(f2) //true
endsWith
:判断当前字符串是否以另外一个给定的子字符串结尾,返回true
或者false
const str='erro type end.';
let f3=str.endsWith('.');
console.log(f3) //true
参数默认值
- 函数参数默认值
function fn(status=false){
console.log(status)
}
fn(true)
fn()
/*
true
false
*/
剩余参数
- 只能出现在最后,且只能使用一次
function fn(...args){
console.log(args)
};
fn(1,2,3,4,5)
//[ 1, 2, 3, 4, 5 ]
展开数组
- 想依次打印数组的值
let arr=['a','b','c'];
console.log(...arr);
//a b c
箭头函数
- 基本使用
//普通函数
function fn(num){
return num+1
};
console.log(fn(2)) //3
//箭头函数
const fn2=n=>n+1;
console.log(fn2(2))//3
箭头函数的this
- 箭头函数不会改变
this
指向,也就是说在箭头函数的外面拿到的this是什么,在箭头函数内部拿到的this也就是什么。
const name='tom'
const people={
name:'lonjin',
say(){
console.log(`this name is ${this.name}`)
},
say2:()=>{
console.log(`this name is ${this.name}`)
}
}
people.say();//this name is lonjin
people.say2()//this name is undefined
对象的扩展
对象字面量的增强
-
对象中属性名和属性值相同就可以缩写
-
函数表达式也可以缩写
-
动态属性名,可以直接使用计算属性名,使用方括号的形式使用
const username='lonjin';
const people={
age:18,
username,
[Math.random()]:123,
}
console.log(people)
//{ age: 18, username: 'lonjin', '0.3692996732470484': 123 }
Object.assign
- 将多个源对象中的属性复制到一个目标对象中;第一个参数为目标对象,如果有相同的,后面的会覆盖之前的值。
let obj={
a:123,
b:456,
}
let obj2={
a:789,
c:234
}
let obj3={
d:123,
c:678
}
// const n=Object.assign(obj,obj2)
//console.log(n)//{ a: 789, b: 456, c: 234 }
const m=Object.assign(obj,obj2,obj3);
console.log(m)
//{ a: 789, b: 456, c: 678, d: 123 }
Object.is
- 判断两个对象是否相等,和严格相等运算符(
===
)类似,但有两个值他们判断起来不太一样:+0
与-0
、NaN
console.log(
//0==false //true
//+0 === -0 // true
// NaN === NaN //false
//Object.is(NaN,NaN) //true
Object.is(+0,-0) //false
)
Proxy
- 代理对象:为对象设置访问代理器
const people={
name:"lonjin",
age:18,
}
let peopleProxy=new Proxy(people,{
//监听读取
get(target,property){
// target为对象
// property为要读取的键
console.log(target,property)
//{ name: 'lonjin', age: 18 } name
//返回读取的内容
return property in target?target[property]:'none'
},
//监听属性设置
set(target,property,value){
/*target---目标对象
property---要设置的键名
value---要设置的新值
*/
if(property=='age'){
if(!Number.isInteger(value)){
throw new TypeError('Value must be a number')
}
}
target[property]=value
}
})
console.log(peopleProxy.name)//lonjin
console.log(peopleProxy.say)//none
// peopleProxy.age='28';//throw new TypeError('Value must be a number')
peopleProxy.age=20;
defineProperty和Proxy的区别
defineProperty
只能监视属性的读写,Proxy
能够见识到更多对象操作。比如属性的删除:
const people={
name:"lonjin",
age:18,
};
let peopleProxy=new Proxy(people,{
deleteProperty(target,property){
//target 为代理对象
//property 为要删除的键名
delete target[property]
}
});
delete peopleProxy.age;
console.log(peopleProxy)
//{ name: 'lonjin' }
Proxy
更好的支持数组对象的监视,vue3
中就采用了Proxy
来实现数据监听
//监听数组push操作
let arr=[];
let newArray=new Proxy(arr,{
set(target,property,value){
target[property]=value;
return true
}
});
newArray.push(1);
newArray.push(2);
console.log(newArray)
//[1,2]
- 更多操作就不在这里写了,推荐大家一篇文章你不知道的 Proxy可以看一下。
Reflect
Reflect
成员方法就是Proxy处理对象的默认实现,Reflect
并非一个构造函数,所以不能通过new()
来调用。
let people={
name:'lonjin',
say:'hello',
}
let peopleProxy=new Proxy(people,{
//如果我们没有定义get方法,那么Proxy会默认一个get方法如下,返回Reflect
get(target,property){
return Reflect.get(target,property)
}
})
Reflect
最大作用是提供了一套完整的对象调用方法
let people={
name:'lonjin',
say:'hello',
}
console.log('name' in people)
console.log(Reflect.has(people,'name'))
console.log(delete people['say']);
console.log(Reflect.deleteProperty(people,'say'));
console.log(Object.keys(people));
console.log(Reflect.ownKeys(people));
Reflect
一共有13个静态方法,更多查看MDN-Reflect
Class
- 基本使用
class People{
constructor(name){
this.name=name
}
say(){
console.log(`this name ${this.name}`)
}
};
let user=new People('lonjin');
user.say()
//this name lonjin
静态方法
- 静态方法可以直接通过类型本身去调用,而实例方法需要通过这个类型构造的实例对象去调用,只需要在方法前面加
static
constructor(name){
this.name=name
}
say(){
console.log(`this name ${this.name}`)
}
static created(name){
return new People(name)
}
};
let user=People.created('lonjin');
user.say()
//this name lonjin
class的继承
class
继承只需要用关键词extends
实现,super
为父类构造函数
class People{
constructor(name){
this.name=name
}
say(){
console.log(`this name ${this.name}`)
}
};
class Child extends People{
constructor(name,age){
super(name);
this.age=age;
}
message(){
console.log(`my name is${this.name},my age is ${this.age}`)
}
};
let tom=new Child('tom',18);
tom.message();
//static
Set数据结构
- set的内部成员是不允许重复的,也就是每一个值在set中都是唯一的。我们平时可以利用
Set
来实现数组去重
//基本使用
let setArray=new Set();
setArray.add(1);
setArray.add(2);
setArray.add(3);
setArray.add(4);
setArray.add(4);
console.log(setArray)
//Set(4) { 1, 2, 3, 4 }
//遍历
setArray.forEach(i=>{console.log(i)});
//1 2 3 4
//获取数量
console.log(setArray.size) //4
//删除(返回true或者false)
console.log(setArray.delete(1))//true
//判断是否有某个值(返回true或者false)
console.log(setArray.has(3))//true
//清空
setArray.clear();
数组去重
Array.from
+set
//方法1
let arr=[1,2,3,4,5,5,6];
let newArr=Array.from(new Set(arr));
console.log(newArr)
//[ 1, 2, 3, 4, 5, 6 ]
- 展开运算符
let arr=[1,2,3,4,5,5,6];
console.log([...new Set(arr)]);
//[ 1, 2, 3, 4, 5, 6 ]
Map
- js的对象本质上是键值对的集合,但是传统上只能用字符串当作键。所以
ES6
提供了Map
数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。与对象最大的区别就是可以使用任意类型作为键,而对象只能使用字符串作为键
let m=new Map();
let n={name:'tom'};
// 设置值
m.set(n,18)
//读取值
console.log(m.get(n))//18
//判断是否有某个值
// m.has();
//删除
// m.delete()
//清空
// m.clear()
//遍历
m.forEach((val,key)=>{
console.log(val,key)
})
Symbol
- 一种全新的原始数据类型, 表示一个独一无二的值。主要解决对象中属性名冲突的问题
//创建一个独一无二的属性名,且外部无法访问到(模拟私有成员)
let name=Symbol();
let people={
[name]:'lonjin',
say(){
console.log(this[name])
}
};
people.say()//lonjin
- 如果我们希望重新使用同一个
Symbol
值,Symbol.for()
方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol
值。如果有,就返回这个Symbol
值,否则就新建一个以该字符串为名称的Symbol
值,并将其注册到全局
//全局注册表
let n1=Symbol.for('name');
let n2=Symbol.for('name');
console.log(n1===n2)//true
- 获取对象中的键时候,如果使用
for in
,就获取不到Symbol
类型的键;必须使用Object.getOwnPropertySymbols()
进行获取。
let people={
[Symbol()]:'tom',
age:18
}
console.log(Object.keys(people))
console.log(JSON.stringify(people))
console.log(Object.getOwnPropertySymbols(people))
/*
[ 'age' ]
{"age":18}
[ Symbol() ]
*/
for of方法
ES6
借鉴其他语言,引入了for...of
循环,作为遍历所有数据结构的统一的方法。
遍历数组
let arr=[1,2,3,4,5];
for(const item of arr){
console.log(item)
}
//1 2 3 4 5
可以使用break
进行退出
//可以使用break进行退出
let arr=[1,2,3,4,5];
for(const item of arr){
console.log(item)
if(item>3){
break
}
}
//1 2 3 4
遍历Set
let n=new Set([1,2,3,4]);
for(const item of n){
console.log(item)
}
//1 2 3 4
遍历Map
可以配合数组结构语法,直接获取键值
let n=new Map();
n.set('name','tom');
n.set('age',18);
for(const [key,value] of n){
console.log(key+'----'+value)
}
/*
name----tom
age----18
*/
可迭代接口
ES6
提供了Iterable
接口,实现了Iterable
接口就是for...of
的前提
let arr=new Set([1,2,3,4,5]);
const iterator=arr[Symbol.iterator]();
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
/*
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 4, done: false }
{ value: 5, done: false }
{ value: undefined, done: true }
*/
实现可迭代接口
- 由于
for...of
方法不能便利普通对象,我们可以手动实现一下
const obj = {
store: ['foo', 'bar', 'baz'],
[Symbol.iterator]: function () {
let index = 0
const self = this
return {
next: function () {
const result = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for(const item of obj){
console.log(item)
}
//foo bar baz
迭代器模式
- 开发一个任务清单应用
// 迭代器设计模式
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
// 提供统一遍历访问接口
each: function (callback) {
const all = [].concat(this.life, this.learn, this.work)
for (const item of all) {
callback(item)
}
},
// 提供迭代器(ES2015 统一遍历访问接口)
[Symbol.iterator]: function () {
const all = [...this.life, ...this.learn, ...this.work]
let index = 0
return {
next: function () {
return {
value: all[index],
done: index++ >= all.length
}
}
}
}
}
Generator
Generator
函数是ES6
提供的一种异步编程解决方案,来避免异步编程中回调嵌套过深。
基本使用
function * fn(){
console.log('1')
yield 100
console.log('2')
yield 200
console.log('3')
yield 300
}
const f=fn();
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
/*
1
{ value: 100, done: false }
2
{ value: 200, done: false }
3
{ value: 300, done: false }
{ value: undefined, done: true }
*/
- 使用
Generator
函数实现iterator
方法
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
[Symbol.iterator]: function * () {
const all = [...this.life, ...this.learn, ...this.work]
for (const item of all) {
yield item
}
}
}
for (const item of todos) {
console.log(item)
}