ES6备忘

花了一个礼拜时间学习了ES6,本文记录了一些常用关键语法备忘。这篇内容比较简单,对于Promise及异步操作之类的本文暂且略过,将在后续实践中展开。

let

  • 块级作用域(构成块级的大括号不能少)有效,声明后不管全局中有没有,都会形成一个封闭作用域
  • 不存在变量提升,使用前需要先声明(注意函数声明,在块级作用域中声明的函数只有在此块级中可以使用)
  • 不允许在相同作用域内,重复声明同一个变量

const

  • 声明的是常量(声明时必须初始化)
  • 块级作用域有效
  • 复合类型时指向数据所在地址(注意将对象设置为const时不能改变其属性的值;将数组设置为const时不能将另一个数组赋值给它,可以在当前数组中对数组元素作操作)
  • 跨模块常量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //moduleA.js
    exports const AA = 123;
    exports const BB = 456;

    //main.js
    import * as moduleA form './moduleA';
    console.log(moduleA.AA);//123
    //another way
    import {AA} from './moduleA';
    console.log(AA);//123

    ES6中,有6种声明变量的方法;
    var、function声明的全局变量是全局对象的属性;
    let、const、class声明的全局变量不属于全局对象的属性

Symbol

  • 新的(Boolean,String,Number,Object,null,undefined之外的第七种)原始数据类型,表示独一无二的值
  • 不是对象,不能添加属性,不能new,可以加参数,可以显示转为字符串,布尔值,但是不能转为数值
    1
    2
    3
    var s = Symbol('foo');
    s //Symbol(foo)
    s.toString //"Symbol(foo)"
  • 每一个Symbol值都是不相等的,可以作为标志符,用于对象的属性名(不能用点运算符),能保证不会出现同名的属性
    1
    2
    3
    4
    let test = {
    [Symbol()]:'test'
    }
    test[Symbol()] //test
  • 通过Object.getOwnPropertySymbols()获取,返回一个数组
  • Symbol.for()为Symbol值登记的名字。使用同一个Symbol值

解构赋值

从数组或对象中提取值(解构),然后按照层级对应位置进行赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Array
let [a,[b],c = 0,d = 33,e,f,g] = [1,[2],3,undefined,null];//a=1,b=2,c=3,d=33,e=null,g=undefined
let [d,e] = new Set([4,5,6]);//d=4,e=5
let [x = 1,y = 2] = [2];//x=2,y=2
[x,y] = [y,x];//交换变量的值

//Object
let {a,b} = {b:11,a:22};//a=22,b=11
let {y:x} = {x:111};//y=111,x=undefined
({x} = {x:1});//()避免js引擎将{}理解为一个代码块,可能导致歧义,尽量不用;
let { log, sin, cos } = Math;

//String(转化为类数组对象)
const [a,b] = 'hi';//a='h',b='i'
const {length:a} = 'hi'//a=2

//function
function add([x=0,y=9]){
return x+y;
}
add([1,2]);//3
add([]);//9

等号右边必须是可遍历的结构;
undefined !== null,只有===undefined的变量为undefined,其余情况均为其值;
对象结构的内部机制,先找到同名属性,再复制给对应的变量,对应的变量获得赋值的结果,同名属性(理解为模式,非变量)并没有获得赋值;
可以将现有对象的方法赋值到某个变量;
若等号右边为Number和Boolean,则先转为对象;

字符串的扩展

模板字符串(运行效率较慢)

模板字符串表示多行字符串,空格和tab保留
静态字符串一律使用单引号或反引号,不使用双引号
动态字符串使用反引号。

1
2
document.querySelector('a').innerHTML = `link${A.linkText}${A.otherProp}`;
`${a} + ${b} = ${a+b}`;

正则的扩展

  • RegExp构造函数参数改变
    1
    var reg = new RegExp(/abc/ig,'i');//等同于new RegExp('abc','i')
  • 字符串对象的正则方法(match、replace、search、split)全部调用RegExp的实力方法
  • u修饰符代表Unicode模式
  • /\u{BBB30}/u,{}表示Unicode字符
  • y修饰符表示全局模式(区别于g修饰符,y只能从剩余的第一个位置开始)

数值的扩展

  • 减少全局性方法,使语言逐步模块化
    • Number.isFinite(),是否非正无穷
    • Number.isNaN()
    • Number.parseInt(),Number.parseFloat()
  • Number.isInteger(),是否为整数(整数浮点数存储方法相同)

数组的扩展

  • Array.from(arr,item => item*item)将类数组对象(有length属性)和可遍历的对象转为真正的数组,第二个参数用法类似于map
  • […arguments]将一些数据结构转为数组
  • Array.of(arg0,arg1)将一组值转换为数组
  • 空位转为undefined

函数的扩展

  • 参数声明变量默认值之后,不能用let,const再次声明
  • 解构赋值时,传参数据类型相对应
  • 参数先对应默认值(运行时执行,默认值设置为undefined表明可以省略),然后解构赋值生效
  • rest参数将多余参数放入数组中,之后不能有其他参数。
  • 有iterator接口的类数组对象可以使用扩展运算符,没有此接口使用Array.from转为真正的数组
  • 箭头函数,this指向定义时的对象(外层代码块this),不可以作为构造函数(本身没有this),不可以使用arguments(用rest),不能作为Generator函数。
    1
    2
    var f = param => returnParam
    var sum = (num1,num2) => {return num1+num2;}
  • 使用尾递归(严格模式),防止栈溢出

对象的扩展

  • 属性、方法简写(Generator函数加*)
  • name作为对象名、方法名(get、set、bound、anonymous、Symbol的描述)
  • Object.is(),同值相等(针对+1-1,NaN)
  • Object.assign(target,source1,source2)浅拷贝,只拷贝源对象的自身属性(不拷贝继承属性和不可枚举属性),遇到同名属性替换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //浅克隆
    function clone(origin) {
    return Object.assign({},origin);
    }
    //深克隆
    function clone(origin) {
    let originProto = Object.getPrototypeOf(origin);
    return Object.assign(Object.create(originProto),origin);
    }
  • Object.setPrototypeOf(target,origin)设置原型对象

Iterator

  • 为不同的数据结构提供统一的访问机制,有Iterator接口(数组、类数组对象String etc..、Set、Map原生具有)就能完成遍历操作。
    1
    2
    3
    4
    5
    6
    7
    8
    class NewIterator {
    constructor(){}
    [Symbol.iterator]() {
    return this;
    }
    }
    //类数组对象调用数组的Iterator方法
    ArrLike.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
  • 具有Iterator接口的数据结构,通过[…arg]或者Array.from(arrayLike)转为数组

Generator

  • Generator函数封装了多个内部的状态
  • 函数返回一个迭代器对象,可以依次遍历Generator函数内部的每一个状态。也是一个iterator对象生成函数
  • 只有调用next方法才会执行
    1
    2
    3
    4
    5
    function* GenDemo() {
    yield "firstState";
    yield "secondState";
    yield* "divideStr"; //相当于for(var value of "divideStr")
    }
  • 遇到yield暂停执行后面的操作,返回紧跟语句后表达式的值
  • next()继续执行,return()返回给定的值并终结函数执行,若有finally,return推迟到finally代码块执行完再执行

class

  • 等同于构造函数,类内部定义的方法不可枚举
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Parent {
    constructor(x,y) {
    this.x = x;
    this.y = y;
    }
    toString() {}
    toValue() {}
    [methodName](){}
    state = {//实例属性
    count:0
    }
    }
  • 可以立刻执行
    1
    2
    3
    4
    5
    6
    7
    8
    let a = new class {
    constructor(name) {
    this.name = name;
    }
    sayName() {
    console.log(this.name);
    }
    }('立刻执行参数');
    1
    a.sayName();//立刻执行参数
  • 不存在变量提升
  • 类和模块的内部,默认就是严格模式
  • 类的继承(先创造父类的实例对象this,然后再通过调用super方法,用子类的构造函数修改this)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Child extends Parent {
    constructor(x,y,color) {
    super(x,y);//父类的构造函数,用来新建父类的this对象,子类继承父类的this对象
    this.color = color;
    }
    toString() {
    return this.color+''+super.toString();//通过super可以引用父类实例的属性和方法,也可以引用父类的静态方法
    }
    }
    Child._proto === parent;//true
    Child.prototype._proto_ === Parent.prototype;//true
    Object.getPrototypeOf(Child) === Parent;//true
  • class内部只有静态方法,没有静态属性(babel已经支持,可用)
  • new.target查看构造函数(子类继承父类时返回子类)

在继承这块ES6与ES5区别

ES6通过引入class,让javascript引擎去实现原来需要我们自己编写的代码
ES5中需要通过中间对象继承,ES6中直接通过extents实现
ES5通过在原型链上添加方法,代码比较分散。ES6直接将方法写在类中
ES5原生构造函数无法继承(子类无法获得构造函数的内部属性),ES6允许继承构造函数定义子类-
ES5先创造子类的实例对象this,再将父类的方法添加到this上面,ES6先创建父类的实例对象this(必须先调用super方法),然后再用子类的构造函数修改this

Module

  • 解决模块化问题,import加载,{}中的变量名必须与被导入模块对外接口的名称相同,import命令具有提升效果。
  • exports输出(必须是对外的接口,可以通过该接口取到模块内部实时的值),as重命名输出,可以用不同的名字输出多次。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import {stat,exists,readFile} from 'fs';//编译时加载
    import {originName as newName} form './test'
    function v1(){}
    function v2(){}
    export {
    v1 as mod1,
    v2 as mod2,
    v2 as mod22
    }
  • 通过指定默认输出,黑盒模块名(一个模块只能有一个默认输出)
    1
    2
    3
    4
    5
    //A.js
    export default function() {}
    //main.js
    import aaa from './A.js'
    aaa();
  • 模块输出实质是输出值的引用(commonjs输出值的拷贝),运用时访问模块取值,不会缓存。export接口输出对象实例时,任意import都是针对同一个对象实例操作。
  • es6 module transpiler(转成CommonJS或者AMD)
    1
    2
    3
    npm install -g es6-module-transpiler
    compile-modules convert file1.js file2.js
    compile-modules convert -o newName.js originName.js

ESLint与Airbnb语法规则

1
2
npm install -g eslint
npm install -g eslint-config-airbnb
1
2
3
4
//.eslintrc
{
"extends":"eslint-config-airbnb"
}
1
eslint index.js

附录一学习资料
阮老师的ECMAScript入门
airbnb编码规范
ECMAScript6 开发体系实践
廖雪峰的官方网站-原型继承、class继承
babel

附录二 现有选型

1
2
3
4
gulp+webpack+gulp-babel+es6
fis3+fis3-paser-babel+es6
react+webpack+es6
typescript+vscode+es6+ts transform

附录三环境配置

  • npm安装

    1
    2
    npm install --save-dev babel-cli
    npm install --save-dev babel-polyfill
  • 创建一个.babelrc

    1
    2
    3
    4
    5
    {
    presets:[
    "es2015",
    ]
    }

    ​ ​


ES6备忘
https://guoningyan.com/2016/05/01/ES6备忘/
作者
Ningyan Guo
发布于
2016年5月1日
许可协议