ES2015
let与const
es2015之前函数作用域只有全局作用域,函数作用域。es2015增加了块级作用域。
- 在块级作用域生效
- 不会变量提升(变量提升:var定义的变量会在全局作用域,函数作用域,提高到当前作用域最顶部)
let
let只在块级作用域生效,比如如下代码,function里面的i可以被外层循环i++所影响,所以最后输出10
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i)
}
}
a[6]() //输出10
如果换成let,函数内的i不会被外层循环影响,则输出6
var a = [];
for(let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i)
}
}
a[6]() //输出6
const
const在let的基础上增加了只读不可更改属性,不可更改主要指声明即赋值,不可更改内存引用地址,但是可以更改数据属性,比如
const a={}
a=1 //报错:Assignment to constant variable.
a.text='hello' //正常不报错
解构
数组解构
数组解构按数组内元素的顺序可以得到相应的值,超出数组长度的元素则返回undefined,如果有赋值,则直接返回赋值
const arr=[100,200,300]
//num1输出100,num2输出200,num3输出300,num4输出undefined
const [num1,num2,num3,num4]=arr
//count1输出100,count2输出200,count3输出300,count4输出400
const [count1,count2,cout3,cout4=400]=arr
const[data1,...rest]=arr //data1输出arr内对应序列号的元素,rest返回一个包含除了data1之外所有元素的数组
对象结构
const person={
name:'Mary',
age:12,
}
const {age,name}=person //age输出12 name输出Mary
const {name:name2,age}=person //age输出12 name2输出Mary
//遇到没有的属性输出undefined,赋值后可以输出
cosnt {gender}=person //gender输出undefined
cosnt {gender='female'}=person //gender输出'female'
模板字符串
- 基础用法
let str=`a\`b\`c` //输出"a`b`c"
/*输出a
bc*/
str=`a\nbc`
const msg='hello'
str=`${hello},Mary` //输出hello,Marry
- 带标签用法及startsWith,endsWith,includesWidth
let name='Tom'
let sex='male'
const str=tagFun`hi, ${name} is a ${sex}`
function tagFun(str) {
console.log(str) //str输出以变量或者常量为切割符的数组
return `123`+str[0] + name +str[1] +sex
}
//标签函数返回值为str的最终值,所以输出:123hi, Tom is a male
console.log(str)
//startsWith 字符串开头是否包含查找的内容,返回布尔值
console.log(str.startsWith('123hi,'))
//endsWith 字符串结尾是否包含查找的内容,返回布尔值
console.log(str.endsWith('male'))
//includes 字符串内是否包含查找的内容,返回布尔值
console.log(str.includes('is'))
默认参数,剩余参数
//默认参数可以直接=赋值
function fn1(first,second=2) {
console.log(first) //输出1
console.log(second) //输出2
}
fn1(1);
//剩余参数可以...表示
function fn2(first,...args) {
console.log(first) //输出1
console.log(args) //输出[2,3,4]
}
fn2(1,2,3,4);
...符号的展开作用
const arr=[1,2,3,4]
const obj1={key1:1,key2:2,key3:3}
const obj2={...obj1,key4:4}
console.log(...arr) //...可以展开数组,所以输出1 2 3 4
console.log(obj2) //...可以展开对象,所以输出{key1:1,key2:2,key3:3,key4:4}
箭头函数
一般的函数this指向一般是谁调用指向谁,情况包括:
- 直接调用:指向window
- 方法调用:指向调用该方法的对象
- new调用:指向new出来的新的实例对象
箭头函数的特点:
- 没有this指向:this来自上下文,即来自外层this的指向
- 不绑定arguments
- 没有prototype属性,不能用作构造器,和new关键字一起会报错
//new的过程
//1.创建一个空对象
var p = {}
// 2. 将空对象p的原型链指向构造器原型
p.__proto__ = Person.prototype
// 3. 将Person()函数中的this指向p。
Person.call(p)
//由于箭头函数没有prototype属性也没有自己的this指向,所以不能用作构造函数
对象
对象字面量增强
const age=12
const person={
age, //等价于age:age
[Math.random()]:234 //等价于person[Math.random()]=234;
}
object.assign
用于对象合并,源对象会把目标对象的相同属性覆盖,比如:
const target={a:1,b:2,c:3}
const source1={a:11,b:22}
const source2={a:111,b:222}
Object.assign(target,source1,source2)
console.log(target)
Proxy
主要用来监控对象,数组的的读写过程
- 对象的监控
const person={
name:'Tom',
age:12,
sex:'male'
}
const personProxy=new Proxy(person,{
get:function(target,key){
if(key==='name'){
return 2000
}
return target[key]; //一定要有返回值
},
set(target,key,value){
if(key==='age' && !Number.isInteger(value)){
throw new TypeError('Must be an integer')
}
}
,
deleteProperty(target, p) {
console.log(`delete ${p}`);
delete target[p]
}
});
console.log(personProxy.name); //输出2000
console.log(personProxy.sex); //输出male
delete personProxy.sex; //delete sex
personProxy.age='aaa' //报错:throw new TypeError('Must be an integer')
- 数组的监控
const list=[]
const listProxy=new Proxy(list,{
set(target,key,value){
//输出[] 0 100,[ 100 ] length 1
console.log(target,key,value)
target[key]=value
return true
}
});
listProxy.push(100)
Reflect
以前的对象操作方法应用方式很多,比如
const person={
name:'Tom',
age:12,
sex:'male'
}
console.log('name' in person) //判断是否存在name属性
console.log(delete person['name']) //删掉name属性
console.log(Object.keys()) //获取所有key
Reflect是为了操作对象提供的新的API,为了统一对象的操作方法。Reflect不是函数对象不可以作为构造函数,所有属性和方法都是静态的。上面那段操作等同于
const person={
name:'Tom',
age:12,
sex:'male'
}
console.log(Reflect.has(person,'name'))
console.log(Reflect.deleteProperty(person,'name'))
console.log(Reflect.ownKeys(person))
class类和类继承
class Person {
constructor(name) {
this.name=name //构造器里面的部分立即执行
}
say(){
console.log(`my name is ${this.name}`)
}
}
class Student extends Person{
constructor(name,number) {
super(name); //super()直接访问父类的构造器
this.name=name
this.number=number
}
hi(){
super.say() //访问父类say方法
console.log(`my school number is ${this.number}`)
}
}
const student=new Student('Tom','123')
student.hi()
//先调用父类的say方法,再输出,所有最后输出结果是
//my name is Tom
//my school number is 123
Set
Set是一个类似于数组的结构,不同的是Set里面的用法不允许重复,所以常常用来数组去重
const arr=[1,2,3,4,1,3,2]
const newArr=[...new Set(arr)] //Set结构转换成数组,也可以Arrary.from(arr)
console.log(newArr) // 输出[1,2,3,4]
Set常用属性如下
const set=new Set();
set.add(1).add(2).add(3).add(4); //add可以链式调用
console.log(set);//输出[1,2,3,4]
console.log(set.size)//类似于数组的length,输出4
set.delete(2) //可以按照value值删掉元素
console.log(set) //输出[1,3,4]
console.log(set.has(2)) //可以按照value判断元素是否存在 输出false
set.clear() //清空
console.log(set) //输出Set(0){}
Map
Map类似于对象数据结构,不同于对象的是:对象只可以用字符串作为键(就算传入其他类型数据,也会转变成字符串),Map可以用任意类型数据作为键
const person={name:'Tom'}
const map=new Map();
map.set(person,'value')
map.set('age',12)
console.log(map) //输出 Map(2) { { name: 'Tom' } => 'value', 'age' => 12 }
map.delete('age')
console.log(map)//输出 Map(1) { { name: 'Tom' } => 'value' }
console.log(map.has('age')) //输出 false
map.clear()
console.log(map) //输出 Map(0) {}
Symbol
Symbol是一个全新的数据类型,表示独一无二的值,主要作用:
- 防止对象属性重复引起冲突
- 定义对象的私有属性
const name=Symbol(); //name赋值Symbol,由于Symbol()独一无二,故可变成类的私有属性
//class是创建对象的模版,由一系列属性和方法构成,用于表示对同一概念的数据和操作。有的属性和方法是对外的,但也有的是私有的
class Person{
constructor() {
this[name]='Tom';
this.age=12;
}
say(){
console.log(`hi,my name is ${this[name]}`)
}
}
const student=new Person();
student.say(); //输出:hi,my name is Tom
console.log(Object.keys(student))//输出:[ 'age' ],name是私有属性不能被外界访问
console.log(Object.keys(student))
//可以通过getOwnPropertySymbols方法获取Symbol属性
console.log(Object.getOwnPropertySymbols(student)) //输出:[ Symbol() ]
Symbol是独一无二的,但是可以通过Symbol.for方法,传参string类型,传同样string的是相等的
let a=Symbol();
let b=Symbol();
console.log(a===b) //输出:false
a=Symbol.for('A');
b=Symbol.for('A');
console.log(a===b) //输出:true
for...of
for...of是es6新增的循环属性,用于循环数组,Set,Map,string等,因为这些类型有迭代器对象,通过调用迭代器对象的next方法可以遍历所有返回值
for...in与for...of的对比应用:
const list=[100,200,300]
const obj={name:'tom',age:12}
const set=new Set(list);
const map=new Map();
map.set('name','tom').set('age',12)
console.log(`for...in开始`)
for(let item in list){
console.log(item) //item表示数组下标,输出:0,1
}
for(let item in obj){
console.log(item) //item表示obj的key,输出:name,age
}
console.log(`for...of开始`)
//for..of随时可以break
for(let item of list){
if(item>200) break
console.log(item) //item表示数组元素,输出:100,200
}
for(let item of set){
if(item>200) break
console.log(item) //item表示数组元素,输出:100,200
}
for(let item of map){
console.log(item) //输出:[ 'name', 'tom' ] [ 'age', 12 ]
}
for(const [key,value] of map){
if(value>10) break
console.log(key,value) //输出:name tom
}
因为对象没有迭代器,所以用for...of遍历会报错,可以用for...in遍历
const obj={name:'tom',age:12}
for(let item of obj){
console.log(item) //报错:obj is not iterable
}
迭代器、生成器
迭代器
没有迭代器的数据结构用for...of会报错,可以手动添加迭代器,这部分不要求熟练掌握,了解迭代器思想即可
/*其他数据结构的Symbol.iterator方法返回一个对象,对象里面有next方法,
next方法每次循环都会被调用,返回一个对象,value代表每次循环的值,done代表是否循环完毕*/
const obj={
name:'tom',age:12,
[Symbol.iterator](){
let index=0,self=this,keys=Object.keys(self)
return {
next(){
return {
value:self[keys[index]],
done:index++ >= keys.length
}
}
}
}
}
for(let item of obj){
console.log(item) //输出:tom,12
}
生成器
Gerator,生成器,用来解决异步编程的回调嵌套,可以控制函数的执行权。基本用法:
function * foo() { //*标识
console.log(111)
yield 100;
console.log(222)
yield 200;
console.log(333)
yield 300;
}
//调用函数的时候不会立即执行,只是生成了一个生成器,调用next方法才会执行,到yield语句暂停,yield后面的值作为返回值
const fn=foo();
console.log(fn.next()) //输出:111,{ value: 100, done: false }
console.log(fn.next()) //输出:222,{ value: 200, done: false }
console.log(fn.next()) //输出:333,{ value: 300, done: false }
console.log(fn.next()) //输出:{ value: undefined, done: true }
上面代码用生成器也可以实现
const obj={
name:'tom',age:12,
[Symbol.iterator]:function * (){
let index=0,self=this,keys=Object.keys(self)
for(const item of keys){
yield self[item]
}
}
}
for(let item of obj){
console.log(item) //输出:tom,12
}
ES2016
includes
includes用于数组元素的特定值查找,相比较indexOf而言可以查找NaN,返回布尔值
const arr=['foo',1,false,NaN]
console.log(`indexOf方法查找开始`)
for(let item of arr){
console.log(arr.indexOf(item)) //输出:0,1,2,-1
}
console.log(`includes方法查找开始`)
for(let item of arr){
console.log(arr.includes(item)) //输出:true,true,true,true
}
ES2017
Object.values、Object.entries、Object.getOwnPropertyDescriptors
- Object.values:获取对象的值,返回数组
- Object.entries:获取对象的键对值,返回数组
- Object.getOwnPropertyDescriptors:获取对象的value描述文件
const obj1={
firstName:'Lei',
lastName:'Wang',
get fullName (){
return `${this.firstName} ${this.lastName}`
}
}
console.log(Object.values(obj1))//输出:[ 'Lei', 'Wang', 'Lei Wang' ]
/*输出:
[
[ 'firstName', 'Lei' ],
[ 'lastName', 'Wang' ],
[ 'fullName', 'Lei Wang' ]
]
*/
console.log(Object.entries(obj1))
const obj2=Object.assign({},obj1)
obj2.lastName='Li'
/*输出:{ firstName: 'Lei', lastName: 'Li', fullName: 'Lei Wang' }
因为Object.assign复制属性的时候把fullName当成普通属性复制,可以用Object.getOwnPropertyDescriptors处理
*/
console.log(obj2)
const des=Object.getOwnPropertyDescriptors(obj1)
const obj3=Object.defineProperties({},des)
obj3.lastName='Wei'
//输出:{ firstName: 'Lei', lastName: 'Wei', fullName: [Getter] }
console.log(obj3)
console.log(obj3.fullName) //输出:Lei Wei
async/await
async/await是Promise的终极解决方案
- async是修饰符,修饰函数方法,被修饰的函数默认变成一个Promise对象,因此可以调用then方法,then方法的参数就是函数的返回值
- await也是一个修饰符,取得await后面的结果才会继续往下执行。如果后面是一个Promise对象,就Promise对象的最终返回值(resolve或者reject的返回值)作为返回值,否则把这个非promise的东西当做await表达式的结果
async function fun(){
let a = await new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(100)
},2000)
});
let b = await function(){
return 'setTimeout'
}();
let c = await 300;
console.log(a);
console.log(b)
console.log(c)
}
fun().then(()=>{
console.log(`then方法执行`)
});
/*停留2秒,输出:100
setTimeout
300
then方法执行
*/