《ES标准入门》&《UNDERSTANDING ECMACHRIPT 6》 读书摘录笔记(上)

Posted chickentang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《ES标准入门》&《UNDERSTANDING ECMACHRIPT 6》 读书摘录笔记(上)相关的知识,希望对你有一定的参考价值。



### 前言
*这两本书应该是目前ES6相关的比较好的了,网上有电子版本(文末有链接)。不过我买了书看,哈哈。这两篇摘录笔记分为上下两部分,本文是上半部分(1-6章),摘录了两本书里一些比较有用的知识点。*

### 目录

> #####1. 块级作用域绑定
> #####2. 字符串与正则表达式
> #####3. 函数的扩展
> #####4. 数组的扩展
> #####5. 对象的扩展
  #####6. 集合(Set、Map)
> 7 . Symbol和Symbol属性

> 8 . javascript中的类

> 9 . Promise、Generator函数、Async函数

> 10 . 代理(Proxy)和反射(Reflection)API

> 11 . 修饰器

> 12 . Module


### 一、块级作用域绑定

块级声明用于声明在指定块级作用域之外无法访问的变量。块级作用域存在于:1、函数内部;2、块中(字符‘{‘和‘}‘之间的区域)。Es6 中存在的两种变量声明就是Let和Const声明。

值得摘录的有以下六点:

##### 1. 不能重复声明

    var count = 9; 
    //抛出语法错误
    let count = 7;

##### 2. Const常量声明必须进行初始化

    const age = 8;
    //语法错误:常量未初始化
    const name;

##### 3. 特殊的For循环
For循环设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

    for(let i = 0;i< 3;i++){
        let i = ‘can not change‘;
        console.log(i);//输出‘can not change‘
    }

##### 4. 暂时性死区
在区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭的作用域。只要在声明之前就使用这些变量,就会报错。

    情况一:
    var tmp = 123;
    if(true){
        tmp = ‘abc‘;//ReferenceError
        let tmp;
    }
    
    情况二:
    function test(x = y,y = 2){
        return [x,y];
    }
    
    test();//报错

    情况三:
    let x = x;//ReferenceError: x is not defined

##### 5. Const声明对象,对象属性可变

##### 6. 彻底冻结函数

    constantize(obj) {//彻底冻结函数
        Object.freeze(obj);
        Object.keys(obj).forEach((key, i) => {
            if (typeof obj[key] === ‘object‘) {
                constantize(obj[key]);
            }
        })
        obj.name = 777;//TypeError: Cannot add property name, object is not extensible
        return obj;
    }


### 二、字符串与正则表达式
Es6加强了对Unicode的支持,并且扩展了字符串对象。正则表达式则增加了修饰符和属性。

值得摘录的有以下几点:

##### 1. codePointAt()与String.fromCodePoint()方法
完全支持UTF-16,接受编码单元的位置而非字符位置作为参数,返回与字符串中给定位置对应的码位,即一个整数值:

    var text = "??a";

    console.log(text.charCodeAt(0));  //55362
    console.log(text.codePointAt(0)); //134017

    判断一个字符是2个字节还是4个字节组成:
    function is32Bit(c){
        return c.codePointAt(0) > 0xFFFF;
    }

    is32Bit("??"); // true
    is32Bit("a"); // false

    console.log(String.fromCodePoint(134071)); // "??"

##### 2. normalize()方法
Es6为字符串添加了一个Normalize()方法,它可以提供Unicode的标准化形式,接受一个参数,指明应用哪种Unicode标准化形式(NFC,NFD,NFKC,NFKD)。只需要记住,在对比字符串之前,一定要先把它们标准化为同一种形式:

    let normalized = values.map((text) => {
        return text.normalize();
    })

    console.log(this[normalize](‘\u01D1‘) === this[normalize](‘\u004F\u030C‘)) // true

##### 3. includes()、startsWith()、endsWith()、repeat()方法

    let tempArray = [1, "4", "uu", 5];
    let tempStr = "test123uuunit582";

    console.log(tempArray.includes(5)); //true
    console.log(tempStr.includes("test123", 1)); //从第二个位置进行匹配

    console.log(tempStr.startsWith("test")); //true
    console.log(tempStr.startsWith("test123", 0)); //从第一个位置进行匹配

    console.log(tempStr.endsWith("12",6)); //从第六个位置进行匹配
    console.log(tempStr.repeat(3));    //test123uuunit582test123uuunit582test123uuunit582

##### 4. 模板字符串
领域专用语言(DSL),可以生成、查询并操作其他语言里的内容,且可以免受注入攻击(XSS、SQL等)。

> A、去掉模板字符串中的换行


        $(‘#list‘).html(`
          <ul>
              <li>first</li>
              <li>second</li>
          </ul>
        `.trim());

> B、引用模板字符串本身


        //写法一
        let str = ‘return ‘ + ‘`ni hao ${name}`‘;
        let func = new Function(‘name‘,str);
        let data = func(‘uct‘);
        //写法二
        let str2 = ‘(name) => `ni hao ${name}`‘;
        let func2 = eval.call(null,str2);
        let data2 = func2(‘uct‘);
    
        console.log(data + ‘  ‘ + data2);

> C、标签模板


        alert`123`;
        //等同于
        alert(123);
        var a = 1,b = 2;
        tag`Hello ${a + b} world ${a * b}`;
        //等同于
        tag([‘Hello ‘,‘ world , ‘‘],1,2);//第一个参数是数组,存放模板中没有变量替换的部分,后续参数是变量。

> D、String.raw()转义模板字符串



        String.raw`Hi\n${3+4}`;//"Hi\\n5"

[关于new Function][newFunction]

[关于eval][eval]

[newFunction]: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function
[eval]:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval

##### 5.U修饰符与Y修饰符
U修饰符,即为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。Y修饰符,叫作“粘连”(Sticky)修饰符,它的后一次匹配都从上一次匹配成功的下一个位置开始。

> A、点(.)字符匹配(匹配除换行符外的所有字符)

        var s = "??";
        /^.$/.test(s); // false
        /^.$/u.test(s); // true
    
> B、返回字符串长度的函数


        function codePointLength(){
            var result = text.match(/[\s\S]/gu);
            return result ? result.length : 0;
        }

> C、y修饰符隐含了头部匹配的标志(^)


        const REGEX = /a/y;
        REGEX.lastIndex = 2;
        REGEX.exec("xaya"); // null
        REGEX.lastIndex = 3;
        const match = REGEX.exec("xaya");
        match.index; // 3
    

        
### 三、函数的扩展

值得摘录的有以下几点:

    
##### 1.函数参数的默认值

> A、参数变量默认声明

        function test(x = 1){
            let x = 2;//error
        }

> B、惰性求值
        let x = 10;
        function testLazy(p = x + 1){
            console.log(p);
        }
    
        testLazy(); // 11
        let x = 12;
        testLazy(); // 13

> C、非尾部设置默认值,参数不可省略

        function f(x = 1,y){
            return [x,y];
        }
        f(,1); //报错

> D、默认参数的临时死区TDZ

        function add(first = second,second){
            return first + second;
        }

        console.log(add(1,1));    // 2
        console.log(add(undefined,1));  // 抛出错误
        //当初次调用add()时,绑定first和second被添加到一个专属于函数参数的临时死区

        //表示调用add(1,1) 时的Javascript 代码
        let first = 1;
        let second = 2;
        //表示调用add(undefined,1) 时的Javascript 代码
        let first = second;
        let second = 1; //所以抛出错误;

        
##### 2.默认参数表达式
初次解析函数声明时不会调用getValue()方法,只有当调用add()函数且不传入第二个参数时才会调用:


        constructor() {
            this.value = 5;
        }

        getValue(){
            return this.value ++;
        }
    
        add(first,second = this.getValue()){
            console.log(‘plus: ‘, first + second );
        }
        ...
        Es6Function.add(1,1); // plus:  2
        Es6Function.add(1);    // plus:  6
        Es6Function.add(1);    // plus:  7
            


##### 3. 只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能严格模式

        const doSomething = (a,b = a,...c) => { //error
            ‘use strict‘;
            //code 
        }

##### 4. 不定参数限制使用

> 每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾。

        function any(a,...b,last){} //报错

> 不定参数不能用于对象字面量Setter中

        let object = {

            //语法错误,不可以在Setter中使用不定参数
            set name(...value){}
        }

> 取数组最大值

        let values = [25,34,11,99];
    
        console.log(Math.max(...values)); // 99


##### 5. 箭头函数

> A、箭头函数中的This值取决于该函数外部非箭头函数的this值,且不能通过call()、apply()或bind()方法来改变this值

        thisScope(){
            setTimeout(()=>{
                console.log(‘id: ‘,this.id);
            },100);
        }
        ...
        var id = 55;
          Es6Function.thisScope.call({id:100}); // id : 100

> B、函数体内不可民使用Arguments对象,该对象在函数体内不存在,可以用Rest参数(...)代替

        testArguments(){
            setTimeout(() => {
                console.log(‘args: ‘,arguments)
            }, 100);
        }
        ...
        Es6Function.testArguments(1,2,5,6); // args:  Arguments(4) [1, 2, 5, 6, callee: (...), Symbol(Symbol.iterator): ?]

##### 6. 尾调用优化

> 尾调用就是指某个函数的最后一步是调用另一个函数,只在严格模式下开启。

        "use strict";

        function doSomething(){
            //优化后,立即返回结果
            return doSomethingElse();
        }
        
        function doSomething2(){
            //无法优化,无返回
            doSomethingElse();
        }

> 非严格模式下实现尾递归优化,用“循环” 替换 “递归”

        function sum(x,y){
            if(y > 0){
                return sum( x + 1,y - 1);
            } else {
                return x;
            }
    
        }
        sum(1,1000000);// Uncaught RangeError: Maximum call stack size exceeded(...)

        //用蹦床函数可以将递归执行转为循环执行
        function trampoline(f){
            while(f && f instanceof Function){
                f = f();
            }
            return f;
        }

        //返回一个函数,然后执行该函数,而不是在函数里调用函数,避免递归,消除调用栈过大问题

### 四、数组的扩展

值得摘录的有以下几点:

##### 1. 扩展运算符(...)

扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署该接口就无法转换

> 替代数组的 Apply方法 

        //Es5 的写法

        function f(x,y,z){}

        var args = [1,2,3];

        f.apply(null,args);

        //Es6 的写法

        function f(x,y,z){}

        var args = [1,2,3];

        f(...args);

> 合并数组

        Es5 合并数组

        var arr1 = [1,2];
        var arr2 = [3,4];
        var arr3 = [5];

        arr1.concat(arr2,arr3);

        Es6 合并数组

        [...arr1,...arr2,...arr3];

> 能够识别 32 位的 Unicode 字符的取字符串长度的方法

        function length(str){
            return [...str].length;
        }
    
        length(‘x\uD83D\uDE80y‘); // 3

##### 3. Array.from()方法,用于将类似数组对象和可遍历对象(包括Set和Map结构)转为真正的数组
Array.from()方法除了支持遍历器接口转换外,还支持类数组对象(类数组对象的本质就是有Length属性)

        Array.from({length:3});
        // [undefined,undefined,undefined] 

> 映射转换,Array.from()的第二个参数用来遍历类数组对象并返回

        translate() {
            return Array.from(arguments, (value) => value + 1);
        }

        console.log(NewArray.translate(1,2,3)); // [2,3,4]

##### 4. 为所有数组添加新的方法


> Array.of(),为了弥补数组构造函数Array()的不足

        Array.of(3,11,8); // [3,11,8]
        Array.of(undefined); // [undefined] , 通过Vue 显示到页面则为 [null]
        Array(3); // [,,]

> copyWithin(),把数组内部指定位置成员复制到其他位置(会覆盖其他成员)

        //将3号位复制到0号位
        [1,2,3,4,5].copyWithin(0,3,4); //[ 4, 2, 3, 4, 5 ]
        // -2相当于3号位,-1相当于4号位
        [1,2,3,4,5].copyWithin(0,-2,-1); //[ 4, 2, 3, 4, 5 ]
        
> find(),返回符合条件的第一个(可能有多个)成员,如果没有就返回undefined

        testFind(){ // 返回 3
            return [1,2,3,4,5].find((value,index,arr)=>{
                return value > 2;
            })
        }

> findIndex(),返回第一个符合条件的成员的位置,如果没有就返回 -1

        testFindIndex(){ // 返回 -1
            return [1,2,3,4,5].findIndex((value,index,arr)=>{
                return value > 6;
            })
        }

> fill(),填充一个数组,接受第二第三个参数(起始位置和结束位置)

        testFill(){
            console.log([‘a‘,‘b‘,‘c‘].fill(8,1,2)); // ["a", 8, "c"]
            return new Array(3).fill(8); //[ 8, 8, 8 ]
        }

> includes(),返回一个布尔值,表示某个数组是否包含给定的值,它与indexOf 比起来更加直观,并且可以判断NaN的情况

        testIncludes(){
            return [1,2,NaN].includes(NaN); // true
        }

> entries(),keys(),values()方法,它们都返回一个遍历器对象

        testEntries(){
            for(let [key,value] of [‘a‘,‘b‘,‘c‘].entries()){
                console.log(key,value); // 0 "a"  1 "b"  2 "c"
            }
            let letter = [‘a‘,‘b‘,‘c‘];
            //手动调用遍历器对象的Next方法进行遍历
            let entries = letter.entries();
            console.log(entries.next().value); //[0, "a"]
            console.log(entries.next().value); //[1, "b"]
            console.log(entries.next().value); //[2, "c"]
        }

### 五、对象的扩展
ECMAScript6 中,为了使某些任务更易完成,在全局Object 对象上引入了一些新方法

值得摘录的有以下几点:

##### 1. 可计算属性名

        let suffix = " name";

        var person = {
            ["first" + suffix]: "Nicholas", // Es5 版本,这里的名称是不可以计算的
            ["last" + suffix]: "Zakas",
        }

        console.log(person["first name"]); // "Nicholas"
        console.log(person["last name"]);    // "Zakas"

##### 2. Object.is(),弥补全等(===)运算符的特殊情况判断不一致

        testObjectIs(){
            console.log(+0 == -0);             // true
            console.log(+0 === -0);         // true
            console.log(Object.is(+0 , -0)); // false
            console.log(NaN == NaN);         // false
            console.log(NaN === NaN);         // false
            console.log(Object.is(NaN , NaN)); // true
        }

##### 3. Object.assign(),只会复制对象自身可枚举属性,忽略enumerable为false的属性

        
> 多个对象复制具有同名属性,排位靠后的源对象会覆盖排位靠前的

        testObjectAssign(){
            let receiver = {};
            Object.assign(receiver,{
                type:‘girl‘,
                name:‘superman‘,
            },{
                type:‘boy‘,
            })
            return receiver.type + " : " + receiver.name; // boy : superman
        }

> 对象浅复制(源对象某个属性是对象,只复制这个对象的引用)

        let target = { a: { b: ‘c‘, d: ‘e‘ } };
        let source = { a: { b: ‘hello‘ } }
        console.log(Object.assign(target, source)); // {a: {b:‘hello‘}}
        // a属性被整体覆盖掉,不会得到 {a : {b:‘hello‘,d:‘e‘}} 的结果

##### 3. Object.values()

> 只返回对象自身可遍历属性

        testObjectValues() { // p 的属性描述对象的 enumerable 默认是 false
            var obj = Object.create({}, { p: { value: 42 } });
            return Object.values(obj); // []
        }

> Object.values 会过滤属性名为Symbol值的属性

        console.log(Object.values({ [Symbol()]: 123, any: ‘abc‘ })); // [‘abc‘]

##### 3. Object.entries()

> 转换对象为真正的Map结构

        testObjectEntries(){
            let obj = { a: ‘bar‘, b: 23 };
            let map = new Map(Object.entries(obj));
            console.log(map.get(‘a‘)); // bar
        }


### 六、集合(Set、Map)
Es6 中的Set和Map核心思想跟Java中的Set和Map类似,Set集合成员是唯一的,没有重复,Set本身是构造函数用来生成Set数据结构;Map类型是一种存储着许多键值对的有序列表,它的键名的判断是通过Object.is()方法来实现的(Set也是通过这个方法来判断两个值是否一致),所以 5 和字符串"5"会被判定为两种类型。

值得摘录的有以下几点:

##### 1. Set

> 添加元素用add()方法,删除用delete()方法,判断存在用has(),清空用clear()

        testSet() {
            let set = new Set();
            set.add(5);
            set.add(‘5‘);
            console.log(set.has(5)); // true
            set.delete(5);
            console.log(set.has(5)); // false
            console.log(set.size); // 1
            set.clear();
            console.log(set.has(‘5‘)); // false
            console.log(set.size); // 0
         }

> 数组去重

        testArrayUniq(){
            let set = new Set([1,2,3,3,3,3,4,4,4,4,5]);
            let arr = [...set];
            console.log(arr); //[ 1, 2, 3, 4, 5 ]
            return arr; //[ 1, 2, 3, 4, 5 ]
          }

> 改变原来的Set结构

        changeSet(){
             let set = new Set([1, 2, 3]);
             //方法二
             set = new Set([...set].map((val) => val * 2));
        
             //方法二
             set = new Set(Array.from(set, val => val * 2)); 
             return set; // [ 4, 8, 12 ]
        }

##### 2. Map
Map结构提供了“值-值”对应,而Object结构提供了“字符串-值”对应,Map结构是一种更加完善的Hash结构实现

> 设置值set(),获取值get(),判断存在has(),删除delete(),清空用clear(),多次对同一个键赋值,后面覆盖前面

        testMap(){
              const m = new Map();
              const o = {p:‘hello uct‘};
        
              m.set(o,‘content‘);
              m.set(o,‘new content‘)
              console.log(m.get(o)); // new content
              console.log(m.has(o)); // true
              m.delete(o);
              console.log(m.has(o)); // false
          }

> Map 和 对象互相转换

        //Map转为对象
        strMapToObj(strMap){//Map to Object 
            let obj = Object.create(null);
            for(let [k,v] of strMap){
                obj[k] = v;
            }
            return obj;
        }
        //对象转为Map
        objToStrMap(obj){// Object to Map
            let strMap = new Map();
            for(let k of Object.keys(obj)){
                strMap.set(k,obj[k]);
            }
            return strMap;
        }

##### 3. WeakMap
WeakMap只接受对象作为键名(Null除外)

> WeakMap 应用的典型场景就是以DOM节点作为键名的场景

        const listener = new WeakMap();

        listener.set(element1,handler1);
        listener.set(element2,handler2);

        element1.addEventListener(‘click‘,listener.get(element1),false);
        element2.addEventListener(‘click‘,listener.get(element2),false);

        //一旦DOM对象消失,与它绑定的监听函数也会自动消失

> 部署私有属性

        const _counter = new WeakMap();
        const _action = new WeakMap();
        
        class Countdown {
            constructor(counter, action) {
                _counter.set(this, counter);//weakmap的私有属性
                _action.set(this, action);  //weakmap的私有属性
            }
            dec() {
                let counter = _counter.get(this);
                if (counter < 1) return;
                counter--;
                console.log(counter);
                _counter.set(this, counter);
                if (counter === 0) {
                    _action.get(this)();
                }
            }
        }
        
        export default Countdown;

### 结语
*本来觉得没有必要写这个笔记,但是,看和写真的是两回事。书中的例子大都是抽象化的,也就是为了让大家看起来直观,去掉了上下文,有些还有错。自己动手写的话(运用实例),印象会非常深刻,在真实开发环境中会想到去用它。笔记的下篇已经在写了,相对上篇,我更期待下篇,NSL...*

### 参考链接



[ECMAScript 6 入门](http://es6.ruanyifeng.com/)

[UNDERSTANDING ES6 英文原版](https://leanpub.com/understandinges6/read)

以上是关于《ES标准入门》&《UNDERSTANDING ECMACHRIPT 6》 读书摘录笔记(上)的主要内容,如果未能解决你的问题,请参考以下文章

es6标准入门pdf

学习笔记ES6标准入门

ES6 标准入门-Module

ES6标准入门 字符串的扩展

ES6标准入门 第一章:简介

重读 ES6 标准入门(第3版)