美文网首页
ES6 标准入门第2版 笔记

ES6 标准入门第2版 笔记

作者: 汪汪仙贝 | 来源:发表于2017-06-14 17:59 被阅读0次


    看这本书是在markdown在做的笔记,更友好的阅读方式访问: github es6.md(https://github.com/EarlyBirdss/Study-Diary/blob/master/es6.md)

    ES6

    https://babeljs.io/repl/ 在线转码


    let


    let 声明一个变量, 只在let命令所在的代码块有效(非常适合在for循环中)

    let没有变量提升, 这意味着typeof不再是百分比安全的操作

    只要在块级作用域中存在let命令,它所在声明的变量就绑定(binding)在这个区域, 不受外界影响

    暂时性死区:在代码块内使用let命令声明变量之前, 该变量都是不可用的

    let 不允许在相同作用域内重复声明同一个变量

    可以使用let很方便的块级作用域 (IIFE)


    const

    用来声明常量,一旦声明,其值不可修改(很多规则跟let一样,理解成继承吧)

    必须一声明就立即初始化,不能留到以后赋值

    对于复杂的数据结构(对象,数组), const只能保证保存该数据的地址不发生变化,不能保证其内部值发生变化。(可以使用Object.freeze()冻结对象)


    变量的解构赋值

    解构: ES6按照一定模式,从数组和对象中提取值,对变量进行赋值,这称为解构

    数组

    var [a,b,c] = [1,2,3]; //a=1,b=2,c=3

    var [head,,third,...tail]=[1,2,3,4]; //tail=[4]

    var [foo] = [];//如果解构不成功,变量的值就等于undefined

    let [a,[b],d] = [1,[2,3],4]; //b=2;不完全解构,解构依然可以成功

    如果等号右边不是数组(可遍历的结构)就会报错;

    默认值:

    let [x,y=1] = [0]; //x=0,y=1;

    let [x,y=1,z] = [0, undefined,2]; //x=0,y=1,z=2;(必须使用undefined/,因为es6内部使用严格等===)

    惰性求值, 如果默认值是表达式, 只用用的时候才求值

    对象

    对象没有次序,变量必须和属性同名或使用以下方式,才能取得正确的值

    var {foo:baz} = {foo: 1, bar: 2}; //baz=1;

    字符串

    因为字符串自带length属性,

    let {length: len} = 'jifag';

    字符串被转换为类似数组的对象

    const [a,b,c,d,e] = 'hello'; //a='h',b='e'...

    解构可以嵌套

    var {p: [x,{y}]} = {p:['hello',{y:'world'}]}; // x='hello',y='world';此时p是模式不是变量名

    函数参数的解构赋值

    酷!

    圆括号问题

    对于编译器来说, 一个式子是模式还是表达式,没有办法从一开始就知道,带来的问题就是, 如果模式中出现圆括号怎么处理。

    建议: 不要在模式中放圆括号

    几种情况下在模式中使用圆括号会报错

    用途!!!!

    交换变量的值 [x,y] = [y,x];

    获取从函数返回的多个值 var [a,b,c] = test(); //test return [1,2,3];

    函数参数的定义

    eg1: 参数是有序值function f([x,y,z]){}; f([1,2,3])

    eg2: 参数是无序值function f({x,y,z}){}; f({x:1,y:2,z=3})

    提取json数据

    函数参数的默认值(好用!!!)

    let {log, sin, cos} = Math; //很方便就能把想用的拿出来


    reg

    u修饰符: 用于处理4个字节的utf=16编码, 如(根本打不出来, 浏览器跟markdown也显示不了好伐, 这个问题忽略)

    y修饰符: ‘粘连’ 跟g一样是全局匹配, 但g是从上一次匹配的剩余位置开始匹配,y限制了只能从剩余位置的第一位匹配

    var str = 'aaa_aa_a', g = /a+/g, y = /a+/y;

    g.exec(str); //执行两次, ['aaa'],['aa']

    y.exec(str); //执行两次, ['aaa'],null


    Array

    () //将类数组(array like)对象和可遍历的对象转化为真正的数组,接受第二次参数类似于数组的map函数Array.from({'0':1, '1': 2, '2': 3}, x => x * x) //[1,4,9];

    扩展运算符 (...)

    Array.of() //将一组值转化为数组 Array.of(1,2,3) //[1,2,3]

    数组实例 Array.prototype.copyWithin(target, start = 0, end = this.length): 在数组内部将指定位置的成员复制到其他位置(覆盖)

    数组实例 Array.prototype.find()和findIndex();

    find(function(value, index, arr){})有点像filter, find只找到第一个符合条件的值就返回这个值 findIndex返回该值的位置

    数组实例 Array.prototype.fill(value, start = 0, end = this.length) //很方便的初始化空数组,如果不是空数组, 数组中已有的元素将被重置 new Array(3).fill(0) //[0,0,0]

    数组实例 Array.prototype.entries(),keys(),values() 返回遍历器对象

    数组实例 Array.includes() 返回布尔值

    数据推导 [for (year of years) if (year > 2000) year];


    函数

    rest参数(...变量名): 替代arguments function add(...value){}

    扩展运算符(...) 像reset参数的逆运算

    console.log(...[1,2,3]) //1 2 3 (1,2,3)

    替代数组的apply方法

    合并数组 [1,2,...more] //more=[3,4];

    与解构赋值结合 const [first, ...rest] = [1,2,3,4];//first=1,rest=[2,3,4];

    箭头函数

    var sum = (num1, num2) => num1 + num2;

    如果函数内代码语句多于一行,要用大括号括起来,并使用return返回

    简化回调函数 [1,2,3].map(x => x * x); //[1,4,9]; [6,1,8].sort((a,b) => a - b); //[1,6,8]; const number = (...nums) => nums; // number(1,2,3)=>[1,2,3]


    对象

    属性的简洁表示法 let obj = {x,test(){}}

    属性名表达式 let obj = {['a' + 'b']: 'ab'}

    属性名表达式与简洁表示法不能同时使用,会报错

    Object.is()用来比较两个值是否严格相等,与严格等(===)的行为基本一致,不同之处只有两个

    1. +0不等于-0

    2. NaN等于自身

    Object.assign(): 用来将源对象的所有可枚举属性赋值到目标对象 //Object.assign(target, source1, source2);


    Symbol

    (研究了大半天终于发现意义在哪里,现在表示当前对象的属性有三种 obj.a、obj['a']、obj[a],一二种是ES5的字符串属性名,第三种是Symbol类型,这样就实现唯一的标识了)

    ES6引用一种新的原始数据类型Symbol表示独一无二的值

    let s = Symbol();typeof s;//symbol没有‘new’

    let s = Symbol('foo'),a={};a[s] = function(){};//不能使用点运算符(a.s),不加引号(a['s']);

    Symbol的参数是没有实际意义的,只表示对Symbol实例的描述,主要是为了方便区分

    symbol可以用来定义一组常量,保证这组常量的值都是不相等的

    魔术字符串:在代码之中多次出现、与代码行程强耦合的某一个集体的字符串或数值。

    消除魔术字符串的常用方便就是把它写成一个常量, 此时比较适合用Symbol

    Symbol类型的属性名,不会出现在for...in、for...of遍历中,也不会被Object.keys(),Object.getOwnPropertyNames()返回。也不是私有属性。Object.getOwnPropertySymbols可以获取指定对象的所有Symbol属性名

    Symbol.for(name):重新使用同一个symbol值

    var s1 = Symbol.for('foo'), s2 = Symbol.for('foo');s1 === s2; //true

    var s1 = Symbol('foo'), s2 = Symbol.for('foo');s1 === s2; //false

    内置的Symbol值


    Proxy和Reflect

    proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,属于“元程序”(对编程语言进行编程)。代理器,拦截器

    作为构造函数Proxy接受两个参数,第一个参数是所要代理的目标对象,第二个参数是配置对象(包含有处理函数),要使Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象

    var proxy = new Proxy({},{

    get: function(target, property){

    return 35;

    }

    });

    proxy.time; //35

    set();利用Proxy,可以将读取属性的操作转变为执行某个函数,从而实现属性的链式调用

    var pipe = (function() {

    var pipe;

    return function(value) {

    get: function(pipeObject, fnName) {

    if(fnName == 'get') {

    return pipe.reduce(function(val, fn) {

    return fn(val);

    }, value);

    }

    pipe.push(window[fnName]);

    return pipeObject;

    }

    }

    });

    Proxy中设置set方法可以拦截某个属性的赋值操作

    let validator = {

    set: function(obj, prop, value) {

    if(prop === 'age'){

    if(!Number.isInteger(value)) {

    throw(new TypeError('The age is not an integer'));

    }

    if(value > 200) {

    throw new RangeError('The age seems invalid');

    }

    }

    obj[prop] = value;

    }

    }

    let person = new Proxy({}, validator);

    person.age = 100;

    person.age = 400;//报错

    apply()方法拦截函数的调用

    has()方法可以隐藏某些属性不被in操作符发现

    construct()方法用来拦截new命令

    deleteProperty()方法拦截delete操作

    defineProperty()方法拦截Object.defineProperty();

    enumerate()方法拦截for...in循环

    ownKeys()方法拦截Object.keys()

    Reflect 将Object某些语言层面的方法部署到Reflect之中


    set

    新的数据结构,类似于数组,成员的值都是唯一的,没有重复的值

    set函数可以接受一个数组(或类数组)let set = new Set([1,2,3,3]); [...set]; //[1,2,3]

    属性和方法set.prototype.size ,add(value), delete(value), has(value), clear()

    遍历操作 keys(),values(),entries(),forEach();前三个返回遍历器。set结构只有键值没有键名,keys的返回值等于values

    WeakSet 内部成员只能是对象,不可遍历,没有clear方法,没有size属性,引用为存储dom节点,不必担心这些节点从文档移除时会引发内存泄漏(没有理解……)

    Map

    理解: Object提供“字符串——值”的对应,Map提供“值——值”的对应

    map接收一个数组作为参数,数组的成员为一个个表示键值对的数组;

    var map = new Map([['name', 'Jack'], ['age', '12']]);

    map.size; //2

    map.has('name'); //true

    map.get('name'); //jack

    map.set('gender', 'female'); //

    map.delete('age');

    map.clear();

    WeakMap: 只接收对象为键名(null除外)。同样应用于dom, 部署私有属性


    Iterator

    遍历器,是一种接口,为各种不同的数据结构提供统一的接口的访问机制,任何数据结构只要部署iterator接口就可以完成遍历操作

    不断调用next方法 返回({value:value, done: Boolean})

    for...of 循环

    一个数据结构只要具有Symbol.iterator属性,就被认为是可遍历的。调用Symbol.iterator方法就可以得到当前结构的遍历函数

    以下三种结构原生具有iterator接口: 数组, 某些类似数组的对象, set和map结构

    var map = new Map([['name', 'aya'], ['gender', 'female']]);

    var iter1= map[Symbol.iterator]();

    iter1.next(); // Object {"done": false,"value": Array ["name","aya"]}

    //类数组调用Symbol.iterator

    let iterable = {

    '0': 1,

    '1': 2,

    '2': 3,

    length: 3,

    [Symbol.iterator]: Array.prototype[Symbol.iterator]

    };

    let ite =  iterable[Symbol.iterator]();

    console.log(ite.next());

    对象没有默认部署iterator接口,是因为哪个属性先遍历哪个后遍历是不确定的。需要开发者手动指定

    // 为对象添加Iterator的一个实例

    let obj = {

    data: ['hello','world'],

    [Symbol.iterator]() {

    const self = this;

    let index = 0;

    return {

    next() {

    if(index < self.data.length) {

    return {

    value: self.data[index++],

    done: false

    };

    }else {

    return {

    value: undefined,

    done: true

    };

    }

    }

    }

    }

    }

    for(let j of obj){

    console.log(j);

    }

    var i = obj[Symbol.iterator]();

    i.next(); //'hello';

    调用Iterator接口的场合

    1. 解构赋值(会默认调用Symbol.iterator方法)

    2. 扩展运算符(...)

    3. for...of,Array.from(),Map、Set、WeakMap、WeakSet,Promise.all(),Promise.race();

    字符串可以访问Sybmol.iterator方法

    for...of(遍历所有数据结构的统一方法)


    Generator

    概念: Generator是ES6提供的一种异步编程解决方案。状态机。执行Generator会返回一个遍历器对象。

    function与函数名之间有一个*号, 函数内部使用yield定义不同的内部状态

    yeild就像是暂停标志,generator.next()执行一次就从就返回一个{value:yeild语句后的值,done:},并暂停向下执行,再执行.next()方法再返回再暂停,直到遇到return语句为止,没有return就放回{value: undefined, done: true};

    yeild后面的表达式,只有当调用next方法,内部指针指向语句时才会执行(惰性求值)

    yeild语句不能出现在普通函数中(foreach改为使用for循环),yield语句如果在表达式中,必须加圆括号

    next的参数,会被当做上一条yield语句的返回值

    for...of,扩展运算符,解构赋值,Array.from

    应用

    1. 异步操作的同步化表达: 处理异步操作,改写回调函数

    function* main(){

    var result = yield request("http://some.url.com");

    var resp = JSON.parse(result);

    doSomething(resp);

    }

    2. 在任意对象上部署Iterator接口

    function* iterEntries(obj) {

    let keys = Object.keys(obj);

    for(let i = 0; i < keys.length; i++) {

    let key = keys[i];

    yield [key, [obj[key]]];

    }

    }

    let testObj = {hello: 'you', world: 'me'};

    for(let [key,value] of iterEntries(testObj)){

    console.log(key, value);

    }

    3. 作为数据结构

    function* doStuff(){

    yield fs.readFile.bind(null, 'hello.txt');

    yield fs.readFile.bind(null, 'work.txt');

    yield fs.readFile.bind(null, 'as-so-on.txt');

    }


    Promise

    一个对象,用来传递异步操作消息

    有两个特点

    1. 对象的状态不受外界干扰。Pending,Resolved(Fulfilled),Rejected。只有异步操作的结果可以决定当前是哪一种状态。

    2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。promise只能从Pending变为Resolved或Pending变成Rejected。只要一种变化发生就不会再发生变化

    实例方法then, catch

    Promise.all() 将多个Promise实例包装成一个新的promise实例 promise.all([p1,p2,p3]);

    Promise.race() 同样将多个promise实例包装成一个新的promise实例 promise.race([p1,p2,p3]);

    区别: all方法,只有参数p全部变成resolved,p才为resolved(rejected同样);race方法,只要有一个参数变成resolved,p就变为resolved(rejected同样);

    Promise.resolve() 将现有对象转为promise对象

    promise对象可以不带参数, 如果希望得到一个新的promise对象,可以直接调用 var p = Promise.resolve();

    Promise.reject() 返回一个新的promise对象,状态为Rejected

    promise.done() 总是处于回调链的尾端,保证抛出任何可能出现的错误

    promise.finally() 跟promise.done最大的区别的是: finally接受普通函数作为参数,无论如何都会执行


    异步操作

    ES6的异步编程: 回调函数,事件监听, 发布/订阅, Promise对象

    异步: 一个任何分为两段,先执行第一段,然后转而这行其他认识,等做好准备再回头执行第二段(不连续的操作)

    Thunk函数: 编译器的传名调用(如var m = 1; f(m+1),//m+1在f内用到才求值,而不是先求值再调用f(2)),实现往事先将函数放到一个临时函数中,再将这个临时函数传入函数体。这个临时函数就叫Thunk函数。

    js是传值调用(var m = 1; f(m+1);// 先求值等于2,在调用f(2);Thunk函数替换的不是表达式而是多参数函数,替换为单参数版本,且只接受回调函数作为参数。


    Class基本语法

    class Point{

    constructor(x, y) {

    this.x = x;

    this.y =y;

    }

    toString(){

    return "x+y= " + this.x + this.y;

    }

    }

    语法糖

    类的内部定义的所有方法都是不可枚举的(跟ES5不同)

    constructor方法是类的默认方法,new之后自动调用该函数

    如果忘记加new调用Class,会报错

    Class表达式

    const MyClass = class Me {

    getClassName(){

    return Me.name;

    }

    }

    调用时使用new MyClass();Me只供Class的内部代码使用

    不存在变量提升

    代码内部为严格模式

    extends 实现继承

    class ColorPoint extends Point {

    constructor(x, y, color) {

    super(x, y);

    this.color = color;

    }

    }

    super: 关键字,指代了父类的实例(即父类的this对象)

    子类必须在constructor方法中调用super, 否则新建实例时会报错。因为如果不调用super方法,子类就得不到this对象

    父类只要用prototype就能被子类继承, 父类可以是任意函数

    对象总是继承其他对象的,所以可以再任意一个对象中使用super关键字

    static 静态方法:在方法前面添加static关键字,不会被实例继承,通过类直接调用

    Class只有静态方法没有静态属性

    new.target属性: 返回new命令所作用的构造函数(在构造函数中使用)


    修饰器

    类的修饰(Class)

    1. 修饰器是一个表达式,用于修改类的行为

    2. 修饰器本质是能在编译时执行的函数

    3. 修饰器函数可以接受3个参数: 目标函数(必要),属性名,该属性的描述对象

    方法的修饰(Class 内部方法)

    不能用于普通的函数(因为函数提升)

    @autobind @override @deprecate


    Module

    Class只是语法糖,并没有解决模块化问题

    Module应对大型程序


    编码风格

    块级作用域let代替var

    全局变量应该只能设置常量,优先使用const

    静态字符串使用单引号或反引号,动态字符串使用反引号(`)

    解构赋值


    使用数组成语对变量赋值,优先使用解构赋值

    函数的参数如果是对象的成员,优先使用解构赋值

    如果函数返回多个值,优先使用对象的解构赋值而不是数组的解构赋值(便于调整顺序)

    对象

    单行定义的对象,最后一个成员不以逗号结尾;多行定义的对象,最后一个成员使用逗号结尾

    对象尽量静态化,如果属性不可避免要添加,使用Object.assign()方法

    如果对象的属性名是动态的,创建对象时使用属性表达式定义

    使用扩展运算符(...)复制数组

    函数

    立即执行函数可以写成箭头函数的形式

    (() => {

    doSomething();

    });

    使用函数表达式的场合尽量用箭头函数代替(绑定了this)

    简单的单行的不会复用的函数用箭头函数

    所有的配置项都应该几种在一个对象,放在最后一个参数,布尔值不可以直接作为参数

    总是使用Class代替需要prototype的操作(class写法更简洁)

    使用extends实现继承

    相关文章

      网友评论

          本文标题:ES6 标准入门第2版 笔记

          本文链接:https://www.haomeiwen.com/subject/pkleqxtx.html