ES6 笔记
1. let & conost
let
let
与var
类似,区别在于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中声明变量只有两种方法,分别是var
和function
,es6除了新添加了const
和let
,还添加了import
和class
关于顶层对象
顶层对象在浏览器中指window
对象,在node中指global
对象,在es5中,顶层对象的属性与全局变量是相同的,在es6中,var
命令和function
命令声明的全局变量依然是顶层对象属性,而let
、const
、class
命令声明的全局变量不是顶层对象属性.
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
,-0
和NaN
有区别
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
有两个特点
- 对象的状态不受外界影响,只有三种状态
Pending
、Fulfilled
、Rejected
- 一旦状态改变就不会再变,任何时候都可以得到这个结果,Promise状态的改变有两种可能从
Pending
状态到Fulfilled
状态或从Pending
状态到Rejected
状态,发生改变后状态凝固,不会再发生变化,一直保持这个结果,这时称为Resolved
Promise基本用法
可使用Promise
构造函数生成一个promise
实例,再使用then
、catch
方法得到promise
的结果
const myPromise=new Promise((res,rej)=>{
if(true){res(1)}
else{rej(0)}
});
myPromise
.then(res=>{})
.catch(err=>{})
Promise方法介绍
Promise.prototype.then()
参考
- 《ES6标准入门》 阮一峰