美文网首页
Understanding ECMAScript6(下)

Understanding ECMAScript6(下)

作者: Michael_lpf | 来源:发表于2017-03-02 12:00 被阅读0次


Part 1: ES6 中的类

基本类的声明
  • ES5 中仿类的构成:
    创建一个构造器,然后将方法指派到该构造器的原型上。
    这被称为“创建自定义类型”。

Paste_Image.png
👆sayName 方法被指派到原型上,接下来用 new 创建了一个 PersonType 的一个实例 person,如此,这个对象是通过原型继承了 PersonType 与 Object 的实例。
  • ES6 通过 class 关键字创建一个类:

    Paste_Image.png
    ES6 的类中,允许使用一个特殊方法 constructor 直接定义一个构造器👆
    constructor 之外的方法名则由我们随意定义。
  • ES6 中的 class,实际上是一个语法糖。
    它实际上创建了并实现了一个和 constructor 一样的函数

    Paste_Image.png
    对其使用 typeof 返回 function 👆
    Paste_Image.png
    sayName 实际上也是 PersonClass.prototype 上的一个函数👆
    也正是这样的特性允许我们将 “类” 和 “自定义类型” 混合使用。

仍有值得主要的不同之处
  • 尽管特别相似,但 “类” 和 “自定义类型” 仍有一些不同之处。
  • 类声明不会被提升,这与函数声明有不同,类声明更像是 let/const 声明,在其声明之前它都存在于 “暂时性死区” 中。
  • 类声明中的代码运行于严格模式中,并且没有退出严格模式的可能。
  • 不使用 new 调用类会报错
    Paste_Image.png

类的表达式
  • 类也有类似函数表达式一样的书写方法
    这个表达式类声明与前面类声明的例子其实没有什么区别👇
    Paste_Image.png
    表达式类声明,同样不会被提升。
    所以,使用哪种声明纯属代码风格问题。
    个人喜欢 class MyClass{} 这样的书写方法,因为这和其它主流编程语言(Java、PHP、Python)更像。

需计算的成员名

类方法也可以使用可计算的名称,用 [] 包裹名称👇

Paste_Image.png
生成器方法

可以为对象定义一个生成器方法。
使用 * 配合方法名称,书写方法和对象字面量中定义生成器是一样的👇

Paste_Image.png
当对象需要存些值并要求可做些简单的迭代时,生成器方法很有效。
于是,我们可以用 Symbol.iterator 来写一个默认迭代器,迭代更方便👇
Paste_Image.png
静态成员
  • 如果想让方法只存在于类自身,无需实例化,这时我们需要的是“静态成员”
  • 在 ES5 中,将方法添加在构造器上而不是构造器的原型上,是模拟静态成员的普遍做法👇
    Paste_Image.png
    ES6 中添加静态方法,使用 static 关键字👇
    Paste_Image.png

派生类
  • 继承了其它类的类称为 “派生类”。
    ES6 使用 extends 创建派生类👇

    Paste_Image.png
    👆如果派生类使用了构造器,则必须在构造器中使用 super() 来访问基类的构造器,否则会报错。
    如果不使用构造器,派生类则会自动调用 super()👇
    Paste_Image.png
  • 只能在派生类(使用了 extend )中使用 super()

  • 屏蔽基类方法
    派生类的方法总是会屏蔽基类中的同名方法。
    这个特性使我们可以在派生类中重新定义它的功能👇


    Paste_Image.png
  • 从表达式中派生类
    ES6 派生类的强大之处在于可以从表达式中派生类。
    只要表达式能够返回一个具有 [[consturctor]]属性以及原型(prototype)的函数👇

    Paste_Image.png
    Rectangle 是一个 ES5 风格的构造器,而 Square 是一个 ES6 的类,由于 Rectangle 具有 [[constructor]] 属性和原型,所以 Square 可以直接继承 Rectangle。


Part 2: Promise


Promise 基础

使用 Promise 对象构造一个实例👇

Paste_Image.png
👆接受一个被称为执行器的函数作为参数,该函数接受两个函数 resolvereject 作为参数。
实例化后,使用 then() 方法指定异步操作结果的回调函数。
Promise 生命周期

Promise 的生命周期初始为 pending state ,此时异步操作尚未结束。一旦操作结束,状态转为 resolved(成功结束) 或 rejected(未成功结束)。

内部的 [[PromiseState]] 属性在这个过程中被设置为 pending 后转换为 resolvedrejected) 以反映 Promise 的状态。
这个属性无法手动判断,但我们可以通过 then() 方法来指定状态改变时可以做的一些事情。


then() 方法和 catch() 方法

then() 方法存在于所有 Promise 实例上,接受两个参数,第一个参数用来指定 Promise 状态由 pending 转为 resolved 时做的事(异步完成了);第二个参数用来指定 Promise 状态由 pending 转为 rejected 时做的事(异步被拒绝了)。

传递给 then() 参数是可选的👇

//  处理完成和拒绝
pro.then(function(content){
    console.log(content);
}, function(error){
    console.log(error);
});
//  只处理完成
pro.then(function(content){
    console.log(content);
});
//  只处理拒绝
pro.then(null, function(error){
    console.log(error);
});

Promise 还有一个 catch() 方法,它的行为和只处理拒绝的例子是一样的👇

pro.catch(function(error){
    console.log(error);
})
//  等同于
pro.then(null, function(error){
    console.log(error);
});

如果被拒绝了,但没有指定如何处理,拒绝还是会静默发生👇


Paste_Image.png
建议始终附加一个拒绝处理函数, 即使该处理程序只是用于打印错误日志  —— Nicholas

catch() 方法还可以这样使用,在 Promise 的执行器中抛出一个错误,外部的拒绝处理函数会被调用👇

Paste_Image.png
串联 Promise

then()catch() 的调用实际上创建并返回了另一个 Promise。

当前一个 Promise 完成或被拒绝时,后一个 Promise 开始工作。
这个特性允许我们使用一组串联起来的 Promise 。

Paste_Image.png

串联中的 Promise 也可以捕获上一个Promise 中抛出的错误👇

Paste_Image.png

若是拒绝处理函数抛出的错误,同样可以在接下来捕获👆


在 Promise 链中返回值

执行器中传递给 resolve 函数的参数会被传递给对应的完成处理函数👇


Paste_Image.png

可以指定完成函数的返回值,这就可以沿着 Promise 链继续传递数据👇


Paste_Image.png
如果是拒绝处理函数也可以完成同样的事情👇
Paste_Image.png

💡 如此看,若有必要,可以用失败的 Promise 传递参数来恢复整个 Promise 继续运行。


响应多个 Promise
  • Promise.all()
    Promise.all() 接受一个可迭代对象(比如数组)作为参数,并返回一个 Promise 。
    可迭代对象的元素中的 Promise 全部完成后,所返回的 Promise 才执行👇
Paste_Image.png
  • Promise.race()
    Promise.race() 同样接受一个可迭代对象作为参数,返回一个 Promise 。
    不同之处是,可迭代对象中有一个 Promise 完成,返回的 Promise 就会执行👇
Paste_Image.png

Part 3:模块封装代码


在 ES6 之前,一个应用的每个 JS 文件所 定义的所有内容都由全局作用域共享。
ES6 的设计目标之一就是 要解决作用域问题。  —— Nicholas
模块特点s
  • 模块中的代码自动运行在严格模式下,并无方法可以退出严格模式。
  • 对于模块外部代码访问的内容,模块必须导出它们。

基本的导出

使用 export 关键字公开功能给其它模块👇

// 导出变量
export let name = 'Pacino';
export const country = 'Italy';
export var address = 'New York';

// 导出类
export class Rectangle{
    constructor(length, width){
        this.length = length;
        this.width = width;
    }
}

// 导出函数
export function sum(num1, num2){
    return sum = num1 + num2;
}

// 声明的函数,不做导出,外部无法访问
function sum(num1, num2){
    return sum = num1 - num2;
}

// 导出一个函数
function foo(){
    return true;
}
// 稍后导出它
export { foo };

👆除了 export 关键字以外,每个声明与平时没什么不同。
💡 无法用这种方法导出匿名函数或匿名类,除非使用 default (稍后再说)


基本的导入

使用 import 关键字引入模块。

// 导入单个绑定
import { sum } form './test.js';

// 导入多个绑定
import { name, country, address } from './test.js';

// 完全导入
// test.js 中暴露出的模块如今都可以使用了
import * as test from './test.js';

重命名的导出和导入

可以使用 as 关键字来为导出和导入的模块重命名👇

// foo 函数被作为 myFunc 导出
function foo(){
    return true;
}
export { foo as myFunc };

👆而在“接收端”(👈我自己起的名字),导入这个函数时则是要接收 myFunc 这个函数而不是 foo 函数了。

在“接收端”,同样可以重命名后导入👇

import { foo as myFunc} from './test.js';

模块默认值

使用 default 关键字指定模块中的默认导出👇

// 这个函数成为了模块中的默认导出内容
export default function foo(){
    return true;
}

// 另一种默认导出的写法
function foo(){
    return true;
}
export { foo as default };

💡 若模块中有多个导出,并需要默认导出,第二种写法是很好的方法。

导入默认内容,去掉 {} 👇

import app from './test.js';

无绑定的导入

之前介绍的导入,都是有指定的内容的,或是默认的内容的,也就是有绑定的导入。
当然我们还可以做无绑定的导入👇

import './test.js';

语法非常简单。
写在导出模块中的内容也不需要添加 export 关键字了。
下面是一个书中的例子:

// 没有导出与导入的模块
Array.prototype.pushAll = function(items) {
    // items 必须是一个数组
    if (!Array.isArray(items)) {
        throw new TypeError("Argument must be an array.");
    }
    // 使用内置的 push() 与扩展运算符
    return this.push(...items);
};

导出模块中的方法,直接拿来用👇

import "./example.js";

let colors = ["red", "green", "blue"];
let items = [];

items.pushAll(colors);


本节完
Understanding ECMAScript6(上)
Understanding ECMAScript6(中)

相关文章

网友评论

      本文标题:Understanding ECMAScript6(下)

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