资讯专栏INFORMATION COLUMN

ECMAScript 6新特性印象之二:面对对象和模块化

darryrzhong / 3048人阅读

摘要:本文参考了以下文章之前的文章新特性印象之一新语法面对对象关键字看上面例子就能明白。定义类的,配合创建新对象。继承非构造器对象的原型是。错误检查继承的目标一定要是个对象或者。的构造器是可改写,但不可枚举。引入了一个标签,负责载入模块。

本文参考了以下文章/PPT:

Use ECMAScript 6 today

Ecmascript 6 Whats next for Javascript

ECMAScript 6: classes

ECMAScript 6 modules: the final syntax

es6features

Javascript Modules

之前的文章:

ECMAScript 6新特性印象之一:新语法

面对对象

1.关键字 Class

class Artist {
    constructor(name) {
        this.name = name;
    }
    perform() {
        return this.name + " performs ";
    }
}

class Singer extends Artist {
    constructor(name, song) {
        super.constructor(name);
        this.song = song;
    }
    perform() {
        return super.perform() + "[" + this.song + "]";
    }
}

let james = new Singer("Etta James", "At last");
james instanceof Artist; // true
james instanceof Singer; // true

james.perform(); // "Etta James performs [At last]"

看上面例子就能明白。注意几个关键字extendssuper

虽然ES6的Class本质上还是语法糖,但这么设计有它的目的。

在ES5中, function关键字承担着三个职责:

定义函数。

定义方法属性。

定义类的constructor,配合new创建新对象。

在ES6中,第2点被属性方法定义(Method definitions)替代,第3点被Class关键字替代。一个关键字只承担一个职责,不再是满屏function,足够清晰了吧?

有几点要注意的:

类的body只能包含属性方法,不能包含属性值。属性值放到constructor方法里。

属性方法可以是生成器,在方法名前家*就可以。

声明类(Class Declaration)并不会被提升(hoisted)。

如果没有指定constructor,那会有个默认调用super的。

2.继承 Extending

继承的几种情况和规则:

不要继承空类, class Foo {},因为:

Foo的原型(prototype)是Function.prototype(所有函数的原型都是这个)。

而Foo.prototype的原型是Object.prototype

这种继承就和函数一样了。

继承nullclass Foo extends null {}

Foo的原型是Function.prototype

Foo.prototype的原型是null

这样Object.prototype的属性方法都不会继承到Foo中。

继承构造器:class Foo extends SomeClass

Foo的原型是SomeClass

Foo.prototype的SomeClass.prototype

这样,类方法属性也会被继承。

继承非构造器(对象):class Foo extends SomeObject

Foo的原型是Function.prototype

Foo.prototype的SomeClass

错误检查:继承的目标一定要是个对象或者null。如果是继承构造器,那么构造器的原型一定要是个对象或者null。

类声明其实创建的是可变let绑定(binding,函数式编程会比较熟悉这个概念)。对于一个类Foo:

Foo的原型是不可改写,且不可枚举的。

Foo的构造器是可改写,但不可枚举。

Foo的原型函数(Foo.prototype.*)是可改写,但不可枚举。

模块

ES6的内置模块系统借鉴了CommonJS和AMD各自的优点:

具有CommonJS的精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点。

类似AMD,支持异步加载和可配置的模块加载。

不仅仅借鉴,ES6还对这些功能进行了优化:

语法比CommonJS更精简。

支持结构的静态分析(静态检查,优化等等)。

循环依赖的支持比CommonJS更好。

1.语法

ES6模块的导出出口形式有两种:命名导出(一个模块多个导出)、标准导出(一个模块一个导出)。

命名导出 Named exports

//-------lib.js----------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//-------main1.js--------
import { sqaure, diag } from "lib";

console.log(square(11)); // 121
console.log(diag(3,4)); // 5

//或者这样,命名导入的名称:
//-------main2.js--------
import * as lib from "lib";
console.log(lib.square(11)); // 121
console.log(lib.diag(3,4)); // 5

标准导出 Default exports

//-------MyClass.js-----
// 注意,`export`的操作对象是表达式,一般是没有名字的。
export default class { ... };

//-------main3.js--------
// 通过模块名字识别
import MyClass from "MyClass";
let inst = new MyClass();

当然,这两种导出也可以混合使用。本质上,标准导出只是指名导出的一种,名称是「default」而已。

就目前了解的来看,ES6的模块导出貌似有些繁琐,还不如CommonJS直接通过object导出利索。

2.设计初衷 Design goals

TC39在讨论ES6的模块化问题时,主要考虑了下面几点:

直接导出优先

静态模块结构

同步/异步载入都要支持

支持循环依赖

第一点就是要简单已用。而静态模块结构更多是出于性能优化、静态类型检查(没错,就是这个,动态语言为什么要有这个,其实还是为了性能)和以后的可能会加入的「宏模板」功能。

3.更多导入/导出写法举例

导入:

// 标准导入,命名导入
import theDefault, { named1, named2 } from "src/mylib";
import theDefault from from "src/mylib";
import { named1, named2 } from "src/mylib";

// 重命名
import { named1 as myNamed1, named2 } from "src/mylib";

// 将导入的模块定义为一个对象
// 模块的每个属性都是该对象的同名方法属性
import * as mylib from "src/mylib";

// 仅读取模块,不导入任何功能
import "src/mylib";

导出:

// 使用关键字**export**导出
export let myVar1 = ...;
export function MyFunc() {...}
export function* myGeneratorFunc() {...}

// 也可以以对象形式导出
const MY_CONST = ...;
function myFunc() { ... }

export { MY_CONST, myFunc }
// 当然,名字不一定要相同
export { MY_CONST as THE_CONST, myFunc as theFunc };

// 支持重导出,即在当前模块导出其他模块的功能 方便hack
export * from "src/other_module";
export { foo, bar } from "src/other_module";

上面说的这些语法,普通的

阅读需要支付1元查看
<