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

前言

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

目录

7 . Symbol和Symbol属性
8 . Javascript中的类
9 . Promise、Generator函数、Async函数
10 . 代理(Proxy)和反射(Reflection)API

七、Symbol和Symbol属性

Es5中包含5种原始类型:字符串、数字型、布尔型、null和undefined。Es6引入了第6种原始类型:Symbol,表示独一无二的值。

值得摘录的有以下几点:

1. Symbol作为属性名
    readProperty(){
        let mySymbol = Symbol();
        let mySymbol2 = Symbol();
        let a = {};
        a[mySymbol]  =  'Hello';//唯一属性
        let b = {
            [mySymbol2]:'nihao',//唯一属性
        }
    }
2. 共享Symbol: Symbol.for()
    // Symbol.for()方法会在全局搜索键为‘uid’的Symbol,每次传入相同的键调用Symbol.for()方法会返回相同的Symbol

    testSymbolFor() {
        let uid = Symbol.for('uid');
        let object = {
            [uid]: "123456",
        };
        console.log(object[uid]);  // 123456
        console.log(uid);          // Symbol(uid) 
        let uid2 = Symbol.for('uid');
        console.log(uid === uid2); // true
        console.log(object[uid2]); // 123456
        console.log(uid2);         // Symbol(uid)
    }
3. 内置的Symbol值

Symbol.isConcatSpreadable,表示该对象使用Array.prototype.concat()时是否可以展开

    //数组的默认行为是可以展开的
     let arr1 = ['c','d'];
    console.log(['a','b'].concat(arr1,'e')); // ["a", "b", "c", "d", "e"]

    //手动设置为False之后就不会展开
    let arr2 = ['c','d'];
    arr2[Symbol.isConcatSpreadable] = false;
    console.log(['a','b'].concat(arr2,'e')); // ["a", "b", Array(2), "e"]

八、Javascript中的类

Es6提供了更接近传统语言(比如Java中的类)的写法,引入了Class(类)这个概念作为对象的模板,通过class关键字可以定义类。

值得摘录的有以下几点:

1. 类的数据类型就是函数,类本身就指向构造函数
    class Point{
        //...
    }

    console.log(typeof Point); // "function"

    Point === Point.prototype.constructor // true
2. 类的实例对象

一个类必须有一个constructor方法 ,如果没有显式定义,一个空的constructor方法会被默认添加

    class test{}

    //等同于
    class test{
        constructor(){}
    }

类的所有实例共享一个原形对象

    class Test {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }

      toString() {
        return `this is x: ${this.x} , this is y: ${this.y} `;
      }
    }

    let out = new Test(2,3);

    console.log(out.toString()); // this is x: 2 , this is y: 3 

    let t1 = new Test(4,5);
    let t2 = new Test(5,4);

    console.log(t1.__proto__ === t2.__proto__); //true

    export default Test;    
3. 访问器属性
    class Rectangle {
      constructor(height, width) {
        this.height = height;
        this.width = width;
        this.rectangleArea = 0;
      }
      get area() { // getter 方法
        this.rectangleArea = this.calcArea();
        return this.rectangleArea;
      }
      set area(value) { // setter 方法
        this.rectangleArea = value;
      }
      calcArea() {
        return this.height * this.width;
      }
    }
    const square = new Rectangle(10, 10);

    //调用方式

    console.log(square.area); // 100

    console.log(square.area = 400); // 400


    export default Rectangle;
4. 私有方法
    const foo = Symbol('foo');
    class TestNewClass{
        constructor(){

        }
        testFoo(){//公有方法
            return this[foo]();
        }
        [foo](){//私有方法
            return 'hello tang!';
        }
    }
    export default new TestNewClass();
5. 静态方法,不需要New,静态属性、实例属性
    class TestStaticClass{

        // prop:2 错误写法

        myProp = 42; // 实例属性

        static myStaticProp = 42; // 静态属性

        constructor(){

        }
        static testFoo(){//静态方法
            return 1;
        }

    }
    export default TestStaticClass;
6. 生成器方法(Generator)
    class MyClass{
        *createIterator(){
            yield 1;
            yield 2;
            yield 3;
        }
    }

    for(let x of new MyClass().createIterator()){
        console.log(x); //1,2,3
    }

    export default new MyClass();
7. 类的继承

关于super():
只可在派生类的构造函数中使用Super(),在构造函数初始化This之前一定要调用Super(),它负责初始化this,如果不想调用Super(),唯一的方法是让类的构造函数返回一个对象。

//父类 class super
export default class Rectangle{
    constructor(length,width){
        this.length = length;
        this.width = width;
    }

    getArea(){
        return this.length * this.width;
    }

    //静态方法
    static getPerimeter(){
        return ( this.length + this.width) * 2;
    }

}

//子类 class extends
import Rectangle from './class_super';

export default class Square extends Rectangle{
    constructor(length){
        console.log(new.target.name); // Square 指向当前正在执行的函数
        super(length,length);
    }

    // 覆写方法
    getArea(){
        return `覆写后的面积是: ${ this.length * this.length}`;
    }

}

// 调用
const instance = new Square(2);

instance.getArea(),// 覆写后的面积是: 4

instance instanceof Square,// true

Square.getPerimeter(2,3),// 10
8. 类的Mixin模式
let SerializableMixin = {
    serialize(){
        return JSON.stringify(this);
    }
}

let AreaMixin = {
    getArea(){
        return this.length * this.width;
    }
}

function mixin(...mixins){
    var base = function(){}
    Object.assign(base.prototype, ...mixins);
    return base;
}

//混合模式,利用函数的继承
export default class Square extends mixin(AreaMixin,SerializableMixin){
    constructor(length){
        super();
        this.length = length;
        this.width = length;
    }
}

//调用

let x = new Square(3);
x.getArea(), // 9
x.serialize(), // {"length":3,"width":3}

九、Promise、Generator函数、Async函数

Nodejs用回调函数代替了事件,使异步编程在Javascript领域更加流行。但当更多程序开始使用异步编程时,事件和回调函数却不能满足开发者想要做的所有事情。为了不让自己陷入回调地狱,可以使用Promise。

值得摘录的有以下几点:

1. Promise生命周期

每个Promise都会经历一个短暂的生命周期,知道这个过程很重要:先是处于进行中(Pending),此时操作尚未完成,所以也是(unsettled)的;一旦异步操作执行结束,Promise则变成已处理(Settled)状态,具体可以是Fulfilled(Promise异步操作成功完成)或者Rejected(由于程序错误或一些其他原因,Promise异步操作未能成功完成)。

所有的Promise都有Then()方法,它接受两个参数:第一个是当Promise的状态变为Fulfilled时要调用的函数,与异步操作相关的附加数据都会传递给这个完成函数(fulfillment function);第二个是当Promise的状态变为Rejected时要调用的函数,基与完成时调用 的函数类似,所有与失败状态相关的附加数据都会这个拒绝函数(Rejection Function)。Promise还有一个Catch方法,相当于传入拒绝处理程序的Then()方法。
每次调用then()方法或者Catch()方法都会创建一个新任务,当Promise被解决(Resolved)时执行。

2. 创建未完成的Promise

//Nodejs示例

let fs = require('fs');

function readFile(filename){
    return new Promise(function(resolve,reject){
        //触发异步操作
        fs.readFile(filename,{encoding:"utf8"},function(err , content){
            //检查是否有错误
            if(err){
                reject(err);
                return;
            }

            //成功读取文件
            resolve(content);

        })
    })
}

let promise = readFile("src/es6_learning/promise/example.txt");

promise.then(function(contents){
    //完成
    console.log(contents);

},function(err){
    //拒绝
    console.log(err.message);
});
3. 全局的Promise拒绝处理程序

如果在没有拒绝处理程序的情况下拒绝一个Promise,那么不会提示失败信息。但是在Nodejs中的Process与浏览器的Window对象中会触发两个事件:

unhandledRejection 在一个事件循环中,当Promise被拒绝,并且没有提供拒绝处理程序时被调用。

rejectionHandled 在一个事件循环后,当Promise被拒绝,并且没有提供拒绝处理程序时被调用。

4. Promise.all()

所有传入Promise.all()方法的Promise只要有一个被拒绝,那么返回的Promise没等所有Promise都完成就立即被拒绝。

let p1 = new Promise(function(resolve,reject){
    resolve(1);
})
let p2 = new Promise(function(resolve,reject){
    reject(2);
})
let p3 = new Promise(function(resolve,reject){
    resolve(3);
})

let p4 = Promise.all([p1,p2,p3]);

p4.catch(function(value){
    console.log(Array.isArray(value));//false
    console.log(value);//2
})

拒绝处理程序总是接受一个值,而非数组。

5. Promise.race()

Promise.race接受含多个受监视Promise的可迭代对象作为唯一参数并返回一个Promise,但只要有一个Promise被解决返回的Promise就被解决,无需等到所有Promise都被完成。

let p1 = new Promise(function(resolve,reject){
    resolve(1);
})

let p2 = Promise.reject(2);

let p3 = new Promise(function(resolve,reject){
    resolve(3);
})

let p4 = Promise.race([p2]);

p4.catch(function(value){
    console.log(value); //2
})
6. async & await
  1. async 返回值是Promise对象,await 命令就是内部then命令的语法糖。
  2. async 函数必须等到内部所有await 命令后面的Promise对象执行完才会发生状态改变,除非遇到Return语句或者抛出错误。

发生错误也不要中断后面的异步操作:

async function f() {
    try{
        await Promise.reject('出错了')
    }catch(e){

    }
    return await Promise.resolve('hello, is me');
}

f().then(v=>console.log(v)) //hello, is me

十、代理(Proxy)和反射(Reflect)API

Proxy用于修改某些操作的默认行为,等同于在语言层面 做出修改,所以属于一种“无编程”(Meta Programming),即对编程语言进行编程。
Refect主要是将Object对象的一些明显属于语言内部的方法放到Refect对象上,修改某些Object对象返回的结果,让其变得更合理,让Object操作都变成函数行为,Reflect对象的方法与Proxy一一对应。

值得摘录的有以下几点:

1. 代理(Proxy)

ES6原生提供的Proxy构造函数,用于生成Porxy实例:

var proxy = new Proxy(target,handler);  

代理Get方法:

class TestProxy{
    constructor(){
        this.proxy = new Proxy({},{
            get: (target,property) => {
                return 2;
            }
        })
    }
}

let tp = new TestProxy();

console.log(tp.proxy.time + " this is time");// this is time
console.log(tp.proxy.name + " this is time name");// this is time name
console.log(tp.proxy.anything + " this is time anything");// this is time anything

要使Proxy起作用必须针对Proxy实例进行操作,而不是针对目标对象。
Proxy支持拦截操作的对象方法有:get,set,has,deleteProperty,ownKeys,getOwnPropertyDescriptor,defineProperty,preventExtensions,getPrototypeOf,isExtensible,setPrototypeOf,apply,construct.

2. 反射(Reflect)

无论Proxy怎么修改默认行为,我们总可以在Reflect上获取默认行为:

class TestReflect{
    constructor(){
        let obj = Object.create(null);

        obj.name = "test name";

        obj.anything = "anything";

        this.loggedObj = new Proxy(obj,{
            get: (target,name) =>{
                console.log('get',target,name);
                return Reflect.get(target,name);//获取默认行为
            },
            deleteProperty(target,name){
                console.log('delete' + name);
                return Reflect.deleteProperty(target,name);//获取默认行为
            },
            has(target,name){
                console.log('has' + name);
                return Reflect.has(target,name);//获取默认行为
            }
        })
    }
}

let tr = new TestReflect();



console.log( `reflect name : ` + tr.loggedObj.name); //reflect name : test name
3. 使用Proxy实现观察者模式
class TestObserve{//Proxy和Reflect实现观察者模式
    constructor(){
        this.queuedObservers = new Set();

        this.set = (target,key,value,receiver) => {
            const result = Reflect.set(target,key,value,receiver);
            this.queuedObservers.forEach(observer => observer());
            return result;
        }

    }
    observe = fn => this.queuedObservers.add(fn);
    observable = obj => new Proxy(obj,{set:this.set});
    print(){
        console.log(`${person.name} , ${person.age}`);
    }
}

let to = new TestObserve();

const person = to.observable({name:'张三',age:20});

to.observe(to.print);

person.name = "pp";

//pp , 20

结语

拖拖拉拉,终于写完了,代码也同步完成,收获不小。

参考链接

ECMAScript 6 入门

UNDERSTANDING ES6 英文原版

猜你喜欢

转载自blog.csdn.net/kingbox000/article/details/80723715
今日推荐