1. let & conost

let
letvar类似,区别在于let命令只在代码块内有效;let有以下特性

  • 不存在变量提升
    var会存在变量提升的现象,如下面两段代码
console.log(a)//Reference error
console.log(a);//Undefined
var a=6;

let所声明的变量必须在声明之后使用

console.log(a)//Reference error
let a=6;
  • 暂时性死区
    只要块级作用域存在let命令,它所声明的变量就"绑定"这个区域,不再受外部影响;在块代码内,如果区块存在let和const命令,则这些命令所声明的变量在声明之前(块内),是不可用的,这被称为暂时性死区(TDZ)
var a=8;
{
    console.log(a)//8
}
var a=8;
{
    //TDZ开始
    console.log(a)//Reference error
    let a=8;
    //TDZ结束
}
  • 不允许重复声明变量
    let不允许在相同的作用域重复声明同一个变量
{
    let a=9;
    let a=8;//error
    var a=7;//error
}

不容易发现的死区,例如

function bar (x=y,y=3){
      console.log(`foo`)
}

const
const实际上保证的并不是变量的值不该东,而是变量指向的内存地址不改动.对于js的6种基本变量,值就保存在变量指向的地址中,所以不可变动,而对于object之类的复合型数据变量,只能保证变量指向的地址中保存的对象指针不变动;
同时const也变量不可提升,存在着暂时性死区

ES6声明变量的6种方法
es5中声明变量只有两种方法,分别是varfunction,es6除了新添加了constlet,还添加了importclass

关于顶层对象
顶层对象在浏览器中指window对象,在node中指global对象,在es5中,顶层对象的属性与全局变量是相同的,在es6中,var命令和function命令声明的全局变量依然是顶层对象属性,而letconstclass命令声明的全局变量不是顶层对象属性.


2. 变量解构

数组的解构赋值

  • 基本用法
    ES6允许按照一定的模式从数组对象中提取值,然后对变量进行赋值,即解构(Destructuring)
let [a,b,c]=[1,2,3]
let [foo,[bar]]=[1,[2]]

只要等号两边的模式相同,左边的变量就会被赋予对应的值,如果未解构成功,则默认为undefined,只要某种数据结构具有Iterator接口,都可采用数组形式的解构赋值

  • 允许默认值
    解构允许指定默认值
let [foo=true]=[]//foo=true
let [x,y='b']=['a']//x='a',y='b'
let [x,y='b']=['a',undefined]//x='a',y='b'
let [x='a']=[null]//x=null

对象的解构赋值
对象与数组的一个重要的不同是变量与属性必须同名才能取得正确的值.

let {bar,foo}={foo:1,bar:2};//foo=1,bar=2

如果变量名和属性名不一致,则必须写成以下这样

let {bar:x,foo}={foo:1,bar:2};//x=2; bar 为reference error

即将:左边的值赋给了右边的值,上面代码中bar是模式,x是变量.

字符串的解构赋值
字符串可以被转换为一个类似数组的对象进行解构赋值

let [a,b,c]='abc'

数值和布尔值解构赋值
解构赋值时,如果等号右边是数值或者布尔值,则会先转为对象

let {toString:s}=1;//s===Number.prototype.toString

函数参数解构

function add([x,y]){
    return x+y;
}

3. 函数扩展

函数参数默认值

  • 基本用法
    ES6之前,不能直接为函数的参数指定默认值,只能在函数体内模拟
function add(x,y){
    y=y||2;
    return x+y;
} 

而在ES6中,允许为函数参数设置默认值

function add(x,y=2){

}
  • 参数默认值的位置
    在函数调用时,如果函数非尾部的参数设置默认值,这个非尾部参数是无法省略的,需要借助undefined.如
function add(x=3,y){
    return x+y;
}
add(,3)//error
add(undefined,3)

应用,利用参数默认值可以指定某一个参数不得省略,如果省略就抛出一个错误

function throwIfMissing(){
    throw new Error(`Missing parameter`)
}

function foo(mustBeProvided=throwIfMissing){
    return mustBeProvided;
}
  • 函数的length属性
    函数的length属性将返回没有指定默认值的参数的个数,注意,若默认值不是尾参数,则返回第一个默认参数前面参数的个数
(function foo(x,y=2,z){}).length\\1

严格模式
如果函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显式设定为严格模式
name属性
函数的name属性将返回函数的名字,在ES6如果将一个匿名函数赋给一个变量,则改变量会变成这个函数的名字.注意若将一个具名函数赋值给一个变量,则name属性都返回这个具名函数原本的名字;

const add =()=>{}
const a=add;
a.name;//add

箭头函数

  • 基本用法
    当箭头后非代码块时候,为一句表达式时,能够隐式地返回计算结果,如
const add=(x,y)=>x+y;

当返回值是一个对象时,需要在对象外添加圆括号,因为大括号会被解析为代码块

const genObj=()=>({a:1,b:2})

当语句多于一句时候,需要添加大括号,并用return显示返回结果

  • 注意事项
    • 箭头函数内的this对象指向的是其定义时所在的父区域
    let a=9;
    function whichA(){
        let a=8;
        const getA=()=>{
            return this.a;
        }
        returen getA();
    }
    whichA();//8
    
    • 不可以使用yield命令
    • 不可以当作构造函数,不可以使用new命令
    • 不可以使用arguments对象,其arguments对象是定义时父区域的arguments对象.如果使用需要用rest参数代替
    function add(){
        const sum=()=>{
            for (const i in arguments) {
                console.log(arguments[i])
            }
        }
        return sum;
    }
    add(1,2,3)();//1,2,3
    add()(1,2,3);//
    

5.数组扩展

扩展运算符

  • 扩展运算符
    扩展运算符...将一个数组转化为用逗号分隔的参数序列;
...[1,2,3]//1,2,3
  • apply方法
    apply方法也能够将一组数组转化为由逗号分割的参数序列
function add (x,y){
    return x+y;
}
let ans=add.apply(null,[1,2]);//3

求最大值

//es5;
Math.max.apply(null,[1,2,3]);
//es6;
Math.max(...[1,2,3]);

将一个数组添加到另一个数组尾部

let arr1=[1,2,3];
let arr2=[4,5,6];
//es5
let arr3=Array.prototype.push.apply(arr1,arr2);
//es6
let arr4=arr1.push(...arr2);
//es5
new (Date.bind.apply(Date,[null,2015,1,1]));
//es6
new Date(...[2015,0,1])
  • 应用
    • 合并数组
    let more=[1,2,3]
    //es5;
    [1,2].cotanct(more)
    [1,2,...more]
    
    • 与解构赋值结合生成数组,注意,用扩展运算符对数组赋值,只能将参数放在最后一位,否则报错
    let a,b;
    //es5
     a=list[0];
     b=list.slice(1);
    //es6;
    [a,...b]=list
    
    • 将字符串转化为数组,并且支持unicode编码
    let s='😊🥺😉😍😘😚'
    console.log([...s])//[ '😊', '🥺', '😉', '😍', '😘', '😚' ]
    
    • 实现了Iterator接口的对象,任何实现了Iterator接口的对象都可以用扩展运算符转化为真正的数组,而没有实现Iterator接口的类似数组的对象无法通过扩展运算符变为数组
    • Map和Set结构,Generator函数
      扩展运算符内部调用的是数据结构的Iterator接口,因此实现了Iterator接口的对象可以使用扩展运算符,如Map,Set;
    let map=new Map([[1,'first'],[2,'second']]);
    [...map]
    
    Generator函数运行后会返回一个遍历器对象,因此也可以使用扩展运算符
      const go =function *(){
          let i = 0;
          while (i < 3) {
              yield i++;
          }
      }
      console.log([...go()])//[0,1,2]
    
  • Array.from()
    Array.from()可将两类对象转为真正的数组:
    • 类似数组的对象(array-like object)
    let arrayLike={
        '0':1,
        '1':2,
        length:2,
    }
    //es5
    let arr1=[].slice.call(arrayLike);
    //es6
    let arr2=Array.from(arrayLike);
    
    • 可遍历的对象,实现了Iterator接口的对象
      此外,Array.from()还可以第二个参数还可以接收一个匿名函数,实现类似于数组map的方法
    Array.from(arrayLike,x=>x*x);
    Array.from(arrayLike).map(x=>x*x);
    
  • Array.of()
    Array.of()将一组参数转化为数组,使用Array.of()来解决Array()该构造函数在参数不同的情况下的行为差异
Array.of(4)//[4]
Array(4)//[,,,]
Array.of(1,2)//[1,2]
Array(1,2)//[1,2]

6.对象扩展

属性表示方式
ES6允许直接将变量和函数作为对象的属性和方法

let a=1;
let obj={a}//obj={a:1}
//等同于
let obj={a:a};

函数简写

let obj={
    method(x,y){
       return {x,y}
    }
}
//等同于
let obj={
    method:function (x,y){
       return {x,y}
    }
}
  • js属性名
    js定义语言有两种方式,分别是
    • 直接使用标识符作为属性名(ES5仅仅此一种)
    obj.foo=true;
    
    • 使用表达式作为属性名
    obj['ab'+'c']=3;
    
  • Object.is()
    Object.is()与严格运算符===基本一致,只是在+0,-0NaN有区别
Object.is(+0,-0);//false;
Object.is(NaN,NaN);//true;
  • Object.assign()
    Object.assign(target,source,source...),该方法将源对象中所有可枚举的属性复制到目标对象中,若目标对象存在该对象,则覆盖,此处的赋值为浅拷贝
let obj1={a:1,b{c:1}};
let obj2=object.assign({},obj1);
obj1.b.c=2;
//obj2.b.c==2
  • 属性的可枚举性
    对象的每一个属性都有一个描述对象Descriptor,用于控制属性的行为,可以使用Object.getOwnPropertyDescriptor()方法查看
let obj={'a':123};
Object.getOwnPropertyDescriptor(obj,'a')
//{ value: 123, writable: true, enumerable: true, configurable: true }

其中enumerable表示该属性是否可枚举

  • 属性的遍历,共有5种遍历对象属性的方法

    • for in
    • Object.keys(obj)返回一个数组,包含对象不含继承的,不含Symbol的所有可枚举属性
    • Object.getOwnPropertyNames(obj)返回一个数组,包含对象所有对象,包含不可枚举属性,不包含Symbol属性
    • Object.getOwnPropertySymbols(obj)返回一个数组,包含对象所有Symbol属性
    • Reflect.ownKeys(obj)返回一个数组,包含对象所有属性,不管是否是Symbol属性和是否可枚举
  • _proto_属性、Object.setPrototypeOf()Object.getPrototypeOf()


7.Symbol

Symbol基本概述
由于ES5中对象的属性都是字符串,可能会造成属性名的冲突,为了解决冲突,ES6引入了类型Symbol用来保证属性名的独一无二.Symbol是js的第7种原始类型,表示独一无二的值,通过Symbol()函数生成
Symbol()接受一个字符串换作为参数,如果参数是一个对象,就会掉用对象的toString方法,将其转化为字符串,然后再生成一个Symbol
Symbol函数的参数只表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值不相等.

let s1=Symbol('foo');
let s2=Symbol('foo');
s1==s2;//false

Symbol作为属性名
每一个Symbol变量都是不同的,用Symbol变量作为属性,可以保证不出现同名的属性,Symbol作为对象属性有三种写法

let mySymbol=Symbol()
//第一种
let obj={};
obj[mySymbol]=123;
//第二种
let a={[mySymbol]:123};
//第三种
let a={};
Object.defineProperty(obj,mySymbol,{value:123})

Symbol属性的应用
魔术字符串指在代码中多次出现、与代码出现强耦合的某一个字符串或者数字值,消除魔术字符串的方式是将其设置为一个Symbol属性


8. proxy

概述 proxy用于修改某些操作的默认行为,Proxy可理解为在目标对象前架设一个拦截层,外界对该对象的访问,必须先通过拦截层,提供了一种机制可以对外界的访问进行过滤和改写;
如下面代码重新定义了对象属性的读取get和设置set

let obj = new Proxy({}, {
    get:function(target,key,receiver){
        console.log(`getting ${key}`);
        return Reflect.get(target,key,receiver)
    },
    set:function(target,key,value,receiver){
        console.log(`setting ${key}`)
        return Reflect.get(target,key,value,receiver)
    }
})
obj.val=123;
//setting val
++obj.val;
//getting val
//setting val

注意,起作用的是Proxy构造函数返回的Proxy对象,而不是作为Proxy构造函数的参数对象
This问题
Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理


9.Reflect

Reflect主要将Object对象的一些明显属于语言内部的方法放到Reflect对象上以及修改某些Reflect方法的返回结果


10.Promise

概述
Promise简单来说是一个容器,保存着某个未来才会结束的事件的结果,从语法上来讲Promise是一个对象,它提供一个统一的API,各种异步操作都可以,Promise有两个特点

  • 对象的状态不受外界影响,只有三种状态PendingFulfilledRejected
  • 一旦状态改变就不会再变,任何时候都可以得到这个结果,Promise状态的改变有两种可能从Pending状态到Fulfilled状态或从Pending状态到Rejected状态,发生改变后状态凝固,不会再发生变化,一直保持这个结果,这时称为Resolved
    Promise基本用法
    可使用Promise构造函数生成一个promise实例,再使用thencatch方法得到promise的结果
const myPromise=new Promise((res,rej)=>{
    if(true){res(1)}
    else{rej(0)}
});
myPromise
    .then(res=>{})
    .catch(err=>{})

Promise方法介绍

  • Promise.prototype.then()

参考

  • 《ES6标准入门》 阮一峰