ES6 介绍
ES6的名称为ESMAScript2015(es2015),是2015年6月份发行的,它是最新ECMAScript的代表版本,一是因为相对与es5变化比较大,二是因为它的发行让标准命名规则发生了变化,ES6更准确的缩写名称应该叫ES2015,
ES6的出现最主要的解决了以下几个问题:
- 解决原有语法上的一些问题或不足(比如let,const)
- 对原有语法进行增强,更加易用(比如解构,展开,参数默认值,模板字符串)
- 全新的对象,全新的方法,全新的功能(promise)
- 全新的数据类型和数据结构(symbol,Set,Map)
let,const与var的区别
1.var 存在变量提升,let 不存在
//let
console.log(c) //Uncaught ReferenceError: Cannot access 'c' before initialization
let c = 100
//var
console.log(c) //underfined
var c =100
因为 let 不存在变量提升,所以上面 let 定义的变量会报错,报错的意思是我们想要打印 c 的值,必须先要初始化,然后再去使用它,必须遵循 先声明,后使用 的使用规则。 var 定义的变量结果为 underfined 代码相当于:
var c;//变量提升,但是值不能提升,所以是underfined
console.log(c) //underfined
c = 100
2.let在同一个作用域下不可以重复定义同一个变量值,而var可以
//let
let c = 100
let c = 200 //报错 Uncaught SyntaxError: Unexpected identifier
//var
var c = 100
var c = 200
console.log(c) //200 正常运行,会覆盖前一个值
3.有严格的作用域,var属于函数作用域,let属于块级作用域
ES5 中作用域有:全局作用域、函数作用域,没有块作用域的概念。 ES6 中新增了块级作用域,块作用域由**{ }**包括,if 语句和 for 语句里面的 { } 也属于块作用域。
//let
function fun(){
let n = 10
if(true){
let n = 20 //与上面的 n 不属于同一个作用域,所以不受影响
}
console.log(n) //10
}
fun()
//var
function fun(){
var n = 10
if(true){
var n = 20 //会覆盖上面的 n 的值
}
console.log(n)//20
}
fun()
4. const
4.1 const 声明的变量为只读的,一旦声明,常量的值就不能改变
const a =2;
a=3;//错误 Uncaught TypeError: Assignment to constant variable.
4.2 const 声明的变量一定要初始化,不能只声明不赋值,
const a; // 错误 Uncaught SyntaxError: Missing initializer in const declaration
//(const声明中缺少初始化程序)
4.3 const 声明的变量只在块级作用域内有效。
{
const a = 3
}
console.log(a) // Uncaught ReferenceError: a is not defined
但是用 const 声明的对象的属性是可以更改的,const 实质上保证的并不是变量的值不得改动,而是变量指向的 内存地址 的值不得改动,对于简单类型数据,值就保存在变量指向的内存地址中,相当于常量。而对于复合型的数据,变量指向的是内存地址保存的是一个指针。const 只能保证指针是不可以被更改,但指针指向的数据结构是可以被改变的。
//以下代码正常执行
const obj = {}
obj.name="xiaoke"
console.log(obj) // {name: "xiaoke"}
const arr = []
arr.push("xiaoke")
console.log(arr) // ["xiaoke"]
数组的解构
- 比如我们要取出arr中的值,我们可以使用下面的方式取出,a1,a2,a3代表了相应位置坐标的值
const arr = ['a','b','c']
const [a1,a2,a3] = arr
console.log(a1,a2,a3)//a b c
- 如果我们只想取索引为2的值,我们可以:
const arr = ['a', 'b', 'c']
const [, , a3] = arr //用逗号进行占位
console.log(a3)//c
- 如果我们想把b和c一起取出来我们可以:
const arr = ['a', 'b', 'c']
const [, ...arr2] = arr
console.log(arr2) //['b','c']
- 如果我们想取出一个数组中不确定存在的元素时,我们可以先给这个值赋值默认值,默认值的意思是数组中不存在此元素时,会输出默认值,存在这个值是输出存在的值:
存在
const arr = ['a', 'b', 'c', 'd']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)//a b c d
不存在
const arr = ['a', 'b', 'c']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)// a b c dd
对象的解构
- 对象的解构和数组的解构很相似,只是对象的解构不是按索引位置进行取值的,而是取出对象中的key值
const obj = { name: "xuke", age: 22 }
const { age } = obj
console.log(age) //22
- 对象和数组一样也可以设置默认值,如果我们想取出一个对象中不确定存在的元素时,我们可以先给这个值赋值默认值,默认值的意思是数组中不存在此元素时,会输出默认值,存在这个值是输出存在的值。
const obj = { name: "xuke", age: 22 }
const { hobby = "eat" } = obj
console.log(hobby) // eat
3.如果我们代码中已经定义了一个与obj中相同的属性名,我们可以使用下面的方式给属性名一个别名,然后再取出
const obj = { name: "xuke", age: 22 }
const name = 'keke'
const { name } = obj
console.log(newname) //会报错:SyntaxError: Identifier 'name' has already been declared
改正
const obj = { name: "xuke", age: 22 }
const name = 'keke'
const { name : newname } = obj
console.log(newname) //xuke
模板字符串
- 我们用反引号来包裹起来字符串: ``
const name = 'xuke'
const str = `my name is ${name}`
console.log(str) // my name is xuke
- 模板字符串支持换行
const name = 'xuke'
const str = `my name
is ${name}`
console.log(str)
//my name
//is xuke
- ${}中可以使用函数表达式,返回最终值
const str = `my age is ${true ? 22 : 10}`
console.log(str) //my age is 22
字符串的扩展方法
- startsWith() 判断是否以某个字符开头
- endsWith()判断是否以某个字符结尾
- includes()判断是否包含某个字符
const message = 'welcome to beijing.'
console.log(message.startsWith('welcome'))//true
console.log(message.startsWith('wel'))//true
console.log(message.endsWith('beijing'))//false
console.log(message.endsWith('.'))//true
console.log(message.includes('bei'))//true
参数默认值
- 当我们给函数传参的时候,我们可以使用以下方法设置默认值
function num(x = 20) {
return x += x
}
//x不传为undefined时 ,会使用我们的默认值 20
console.log(num()) //40
//传值的时候会使用我们传的值
console.log(num(10)) //20
注意:我们对参数设置默认值的时候,要把带有默认值的参数放到最后面
//错误的写法
function num(a = 20, x) {
return a + x
}
//正确的写法
function num(a , x = 20) {
return a + x
}
展开运算符(...)
- 合并数组
let a = [1,2,3];
let b = [4,5,6];
let c = [...a,...b]; // [1,2,3,4,5,6]
- 替代apply
function f(a,b,c){
console.log(a,b,c)
}
let args = [1,2,3];
// 以下三种方法结果相同
f.apply(null,args)
f(...args)
f(1,2,3)
//===========
function f2(...args){
console.log(args)
}
f2(1,2,3) // [1,2,3]
function f3(){
console.log(Array.from(arguments))
}
f3(1,2,3) // [1,2,3]
- 浅拷贝
//数组
var a = [1,2,4]
var b = [...a]
a.push(6)
console.log(b) // [1,2,4]
//对象
var a = {a:1}
var b = {...a}
a.a = 5
console.log(b.a) // 1
箭头函数
使用
const fun = function () {
return 'hello'
}
const fun2 = () => 'hello'
console.log(fun()) // hello
console.log(fun2())// hello
//===
const nums = [1, 2.3, 2, 46, 6, 4, 3, 2]
const result = nums.filter(num => num % 2 === 0)
console.log(result)//[ 2, 46, 6, 4, 2]
箭头函数不会改变this的指向
const Person = {
name:'xuke',
say1:function(){
console.log(this.name) //xuke 指向Person
},
say2:()=>{
console.log(this.name) //undefined 指向window
}
}
Person.say1()
Person.say2()
setTimeOut情况:
const Person = {
name: 'xuke',
say1: function () {
setTimeout(function () {//setTimeOut函数体里面的函数题被放到全局作用域去调用
console.log(this.name) //undefined
}, 1000)
setTimeout(() => {//setTimeOut函数体里面的箭头函数始终指向当前作用域中的this
console.log(this.name) //xuke
}, 1000)
}
}
Person.say1()
对象字面量
- 当我们对象中的
属性名和值相等的时候,我们可以把值省略:
const name = 'xuke'
//普通用法
const obj1 = {
name:name,
age:12
}
//字面量用法
const obj2 = {
name,
age:12
}
console.log(obj1) //{ name: 'xuke', age: 12 }
console.log(obj2) //{ name: 'xuke', age: 12 }
- 当我们想用表达式生成对象的属性名的时猴,我们可以在
[]中写入我们的表达式
//es2015之前
const name = 'xuke'
const obj = {
name,
age:12
}
const obj2 = {}
obj2[obj.name] = 'keke'
console.log(obj2) //{ xuke: 'keke' }
//es2015
const name = 'xuke'
const obj = {
name,
age:12
}
const obj2 = {
[obj.name]:'keke'
}
console.log(obj2) //{ xuke: 'keke' }
Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性覆盖前面的属性
const source = {
a:123,
b:123
}
const target ={
a:456,
c:111
}
console.log(Object.assign(target,source)) //{a:123,c:111,b:123}
还可用于对象的浅拷贝,
const source = {
a:123,
b:123,
name:{
cc:'ccc'
}
}
const target = Object.assign({},source)
target.a = 333
console.log(target) //{ a: 333, b: 123 }
console.log(source) //{ a: 123, b: 123 }
Proxy
proxy真的用处很大,可是我项目中很少用到,总结一下。
proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截.
1. 语法
var proxy = new Proxy(target, handler);
new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为
2. 基本用法
var target = {
name: 'poetries'
};
var logHandler = {
get: function(target, key) {
console.log(`${key} 被读取`);
return target[key];
},
set: function(target, key, value) {
console.log(`${key} 被设置为 ${value}`);
target[key] = value;
}
}
var targetWithLog = new Proxy(target, logHandler);
targetWithLog.name; // 控制台输出:name 被读取
targetWithLog.name = 'others'; // 控制台输出:name 被设置为 others
console.log(target.name); // 控制台输出: others
targetWithLog 读取属性的值时,实际上执行的是 logHandler.get :在控制台输出信息,并且读取被代理对象 target 的属性。
在 targetWithLog 设置属性值时,实际上执行的是 logHandler.set :在控制台输出信息,并且设置被代理对象 target 的属性的值
3. 保持只返回一个值
// 由于拦截函数总是返回35,所以访问任何属性都得到35
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
4. Proxy 实例也可以作为其他对象的原型对象
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截
5. Proxy的作用
- 拦截和监视外部对对象的访问
- 降低函数或类的复杂度
- 在复杂操作前对操作进行校验或对所需资源进行管理
6.Proxy使用场景
- 实现私有变量
var target = {
name: 'poetries',
_age: 22
}
var logHandler = {
get: function(target,key){
if(key.startsWith('_')){
console.log('私有变量age不能被访问')
return false
}
return target[key];
},
set: function(target, key, value) {
if(key.startsWith('_')){
console.log('私有变量age不能被修改')
return false
}
target[key] = value;
}
}
var targetWithLog = new Proxy(target, logHandler);
// 私有变量age不能被访问
targetWithLog.name;
// 私有变量age不能被修改
targetWithLog.name = 'others';
例1:在下面的代码中,我们声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey
var api = {
_apiKey: '123abc456def',
/* mock methods that use this._apiKey */
getUsers: function(){},
getUser: function(userId){},
setUser: function(userId, config){}
};
// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);
// get and mutate _apiKeys as desired
var apiKey = api._apiKey;
api._apiKey = '987654321';
很显然,约定俗成是没有束缚力的。使用 ES6 Proxy 我们就可以实现真实的私有变量了,下面针对不同的读取方式演示两个不同的私有化方法。第一种方法是使用 set / get 拦截读写请求并返回 undefined:
let api = {
_apiKey: '123abc456def',
getUsers: function(){ },
getUser: function(userId){ },
setUser: function(userId, config){ }
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
get(target, key, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} is restricted. Please see api documentation for further info.`);
}
return Reflect.get(target, key, proxy);
},
set(target, key, value, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} is restricted. Please see api documentation for further info.`);
}
return Reflect.get(target, key, value, proxy);
}
});
// 以下操作都会抛出错误
console.log(api._apiKey);
api._apiKey = '987654321';
例2:让我们从一个简单的类型校验开始做起,这个示例演示了如何使用 Proxy 保障数据类型的准确性
let numericDataStore = {
count: 0,
amount: 1234,
total: 14
};
numericDataStore = new Proxy(numericDataStore, {
set(target, key, value, proxy) {
if (typeof value !== 'number') {
throw Error("Properties in numericDataStore can only be numbers");
}
return Reflect.set(target, key, value, proxy);
}
});
// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";
// 赋值成功
numericDataStore.count = 333;
例3:对于那些调用频繁、运行缓慢或占用执行环境资源较多的属性或接口,开发者会希望记录它们的使用情况或性能表现,这个时候就可以使用 Proxy 充当中间件的角色,轻而易举实现日志功能
let api = {
_apiKey: '123abc456def',
getUsers: function() { /* ... */ },
getUser: function(userId) { /* ... */ },
setUser: function(userId, config) { /* ... */ }
};
function logMethodAsync(timestamp, method) {
setTimeout(function() {
console.log(`${timestamp} - Logging ${method} request asynchronously.`);
}, 0)
}
api = new Proxy(api, {
get: function(target, key, proxy) {
var value = target[key];
return function(...arguments) {
logMethodAsync(new Date(), key);
return Reflect.apply(value, target, arguments);
};
}
});
api.getUsers();
Reflect用法
概述
Reflect是为操作对象而提供的新API,那么我们为什么要去使用它呢?将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法,比较方便,可读性更强。
1. Reflect.has(obj,name) 判断对象中的属性是否存在
const object = {
name:'xuke',
age:22
}
console.log(Reflect.has(object,'name'))//true
2. Reflect.set(target,propName,propValue)
const object = {
name:'xuke',
age:22
}
console.log(Reflect.set(object,'ww','dd'))//true object中不存在会直接添加
console.log(Reflect.set(object,'name','dd'))//true object中存在会覆盖
console.log(object) //{ name: 'dd', age: 22, ww: 'dd' }
3. Reflect.set(target,propName)
const object = {
name:'xuke',
age:22
}
console.log(Reflect.get(object,'name'))//xuke 属性名存在直接返回
console.log(Reflect.get(object,'ww'))//undefined 属性名不存在返回undefined
4. Reflect.deleteProperty(obj, name) 删除属性
const object = {
name:'xuke',
age:22
}
Reflect.deleteProperty(object,'name')
console.log(object) //{age:22}
5.Reflect.ownKeys (target) 用于返回对象的所有属性
const object = {
name:'xuke',
age:22
}
console.log(Reflect.ownKeys(object)) //[ 'name', 'age' ]
6. 上面列举了比较常用的方法,当proxy和Reflect配合起来一起使用:
const object = {
name:'xuke',
age:22
}
const proxy = new Proxy(object,{
get:(target,property)=>{
return Reflect.get(target,property) //获取被访问的属性
},
set:(target,property,value)=>{
Reflect.set(target,property,value) //设置被访问的属性与新值
}
})
console.log(proxy.name) //xuke
proxy.name = 'xiaohaha'
proxy.hobby = 'eat'
console.log(object) //{ name: 'xiaohaha', age: 22, hobby: 'eat' }
Set()
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
1.add方法
const s = new Set()
//通过add向Set中添加元素
//由于add()返回的是一个Set对象,所以我们可以链式调用
s.add(1).add(2).add(3)
console.log(s) //Set(3) { 1, 2, 3 }
2.遍历
const s = new Set()
s.add(1).add(2).add(3)
s.forEach(num=>console.log(num)) //1 2 3
for(let num of s){console.log(num)} //1 2 3
3.获取Set长度
const s = new Set()
s.add(1).add(2).add(3)
console.log(s.size) //3
4.has方法
const s = new Set()
s.add(1).add(2).add(3)
//判断Set对象中是否存在某个值
console.log(s.has(22)) //false
5.delete方法
const s = new Set()
s.add(1).add(2).add(3)
//删除Set集合中的元素
s.delete(2)
console.log(s) //Set(2) { 1, 3 }
6.clear方法
const s = new Set()
s.add(1).add(2).add(3)
//清空集合中的元素
// s.clear()
// console.log(s) //Set(0) {}
7.去重
const arr = [2,34,2,4,12,34]
console.log([...new Set(arr)])
console.log(Array.from(new Set(arr)))
Map()
Map 是 ES6 中新增的数据结构,Map 类似于对象,但普通对象的 key 必须是字符串或者数字,而 Map 的 key 可以是任何数据类型...
//初始化
const map = new Map()
1.可以当作普通对象使用
const map = new Map()
map.set('name','xuke')
console.log(map) //Map(1) { 'name' => 'xuke' }
2.键值可以存放对象
const map = new Map()
const obj1 = {
a:12,
b:11
}
map.set(obj1,'obj')
console.log(map) //Map(1) { { a: 12, b: 11 } => 'obj' }
3.键值可以存放函数
const map = new Map()
function a(){
return 100
}
map.set(a,'a')
console.log(map) //Map(1) { [Function: a] => 'a' }
和set一样具有下面的方法
map.has(a)
map.delete(a)
map.clear()
4.遍历
const map = new Map()
function a(){
return 100
}
map.set(a,'a')
//方法1
map.forEach((value,key)=>{
console.log(key) //[Function: a]
console.log(key()) //100
console.log(value) //a
})
//方法2
for(let item of map){
console.log(item) //[ [Function: a], 'a' ]
console.log(item[0]()) //100
console.log(item[1])//a
}
Symbol 数据类型
Symbol是由ES6规范引入的一项新特性,它的功能类似于一种标识唯一性的ID。
1.创建symbol实例
let s1 = Symbol()
在调用Symbol()函数时传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息
let s2 = Symbol('another symbol')
2.有属于自己的symbol类型
typeof s1 // 'symbol'
3.唯一性
let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
s1 === s2 // false
s2 === s3 // false
4.实现相同的symbol
const sy1 = Symbol.for('xuke')
const sy2 = Symbol.for('xuke')
sy1===sy2 //true
5.应用场景
1.Symbol可用于对象属性的定义和访问
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
[PROP_NAME]: "xuke"
}
obj[PROP_AGE] = 22
obj[PROP_NAME] // 'xuke'
obj[PROP_AGE] // 22
2.Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
let obj = {
[Symbol('name')]: 'xuke',
age: 18,
title: 'Engineer'
}
//1.
Object.keys(obj) // ['age', 'title']
//2.
for (let p in obj) {
console.log(p) // 分别会输出:'age' 和 'title'
}
//3.
Object.getOwnPropertyNames(obj) // ['age', 'title']
//4.
JSON.stringify(obj) // {"age":18,"title":"Engineer"}
我们es6提供了一些专门针对Symbol的API:
/ 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
3.使用Symbol来替代常量
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
function handleFileResource(resource) {
switch(resource.type) {
case TYPE_AUDIO:
playAudio(resource)
break
case TYPE_VIDEO:
playVideo(resource)
break
case TYPE_IMAGE:
previewImage(resource)
break
default:
throw new Error('Unknown type of resource')
}
}
4.使用Symbol定义类的私有属性/方法
文件a.js:
const PASSWORD = Symbol()
class Login {
constructor(username, password) {
this.username = username
this[PASSWORD] = password
}
checkPassword(pwd) {
return this[PASSWORD] === pwd
}
}
export default Login
文件b.js
import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456') // true
login.PASSWORD // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!
由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果
for...of
1.遍历数组
const arr = [2, 3, 2, 33, 22, 223, 21, 3]
for (let item of arr) {
console.log(item)
}
2.可用 break 终止循环
const arr = [2, 3, 2, 33, 22, 223, 21, 3]
for (let item of arr) {
if (item > 10) break
console.log(item)
}
3.遍历Set()
const set = new Set([2, 3, 1, 2, 34, 3])
for (let item of set) {
console.log(item) //2 3 1 34
}
4.遍历Map
const map = new Map()
map.set('name','xuke')
map.set('age','12')
for (let item of map){
console.log(item) //[ 'name', 'xuke' ] [ 'age', '12' ]
}
//改进
//用数组解构的方式取出key value 方便使用
for (let [key,value] of map){
console.log(key) //name age
console.log(value)//xuke 12
}
ES2016(ES7)
1.数组的includes方法
const arr =[2,3,2,13,23,45]
//原始方法
console.log(arr.indexOf(3)) //1
console.log(arr.indexOf(300)) //-1
//includes方法
console.log(arr.includes(3))//true
console.log(arr.includes(300))//faalse
2.指数运算符
表示2的10次方:
//原始
console.log(Math.pow(2,10)) //1024
//es2016
console.log(2 ** 10) //1024
console.log(3 ** 10) //59049
ES2017(ES8)
1.object.values
const obj={
name:'xuke',
age:22
}
//与object.keys类似,object.keys返回所有`键`组成的数组,
//Object.values返回的对象中的所有`值`组成的数组,
console.log(Object.values(obj)) //[ 'xuke', 22 ]
2.Object.entries
Object.entries,返回对象的键值对组成的组成的数组,
const obj={
name:'xuke',
age:22
}
console.log((Object.entries(obj))) //[ [ 'name', 'xuke' ], [ 'age', 22 ] ]
//遍历方法
for(let [key,value] of Object.entries(obj)){
console.log(key) //name age
console.log(value) //xuke 22
}
3.string方法 填充字符串
padStart()和padEnd()
let str = 'xuke'
//表示用 "=" 填充到 6位长度 的字符串("="在字符串的`前面`填充)
console.log(str.padStart(6,'=')) //==xuke
//表示用 "=" 填充到 6位长度 的字符串("="在字符串的`后面`填充)
console.log(str.padEnd(6,'=')) //xuke==
const str1 = 'xuke'
const str2 = 'is'
const str3 = 'beautiful'
console.log(str1.padStart(26,'-'))//----------------------xuke
console.log(str2.padStart(26,'-'))//------------------------is
console.log(str3.padStart(26,'-'))//-----------------beautiful
4.在函数参数中添加尾逗号
function sum(a,b,){
return a+b
}
console.log(sum(1,4)) // 5
const array = [2,3,1,5,45,32,]//方便数组的分割
console.log(array)
5.async/await
async await是promise的语法糖,更加方便的处理异步,增加代码的可读性,详细介绍看我的专本总结文章 Promise,Generator,async/await学习总结基本用法