ES6

ES6:类

2016-12-12  本文已影响139人  开车去环游世界

引用:http://www.tuicool.com/articles/BFf6jiB

从本质上说,ES6的classes主要是给创建老式构造函数提供了一种更加方便的语法,并不是什么新魔法 —— Axel Rauschmayer,Exploring ES6作者
从功能上来讲,class声明就是一个语法糖,它只是比我们之前一直使用的基于原型的行为委托功能更强大一点。本文将从新语法与原型的关系入手,仔细研究ES2015的class关键字。文中将提及以下内容:

在此过程中,我们将特别注意 class 声明语法从本质上是如何映射到基于原型代码的。

退一步说:Classes不是什么

JavaScript的『类』与Java、Python或者其他你可能用过的面向对象语言中的类不同。其实后者可能称作面向『类』的语言更为准确一些。
在传统的面向类的语言中,我们创建的类是对象的模板。需要一个新对象时,我们实例化这个类,这一步操作告诉语言引擎将这个类的方法和属性复制到一个新实体上,这个实体称作实例。实例是我们自己的对象,且在实例化之后与父类毫无内在联系。
而JavaScript没有这样的复制机制。在JavaScript中『实例化』一个类创建了一个新对象,但这个新对象却不独立于它的父类。
正相反,它创建了一个与原型相连接的对象。即使是在实例化之后,对于原型的修改也会传递到实例化的新对象去。
原型本身就是一个无比强大的设计模式。有许多使用了原型的技术模仿了传统类的机制,class便为这些技术提供了简洁的语法。
总而言之:

类基础:声明与表达式

我们使用class 关键字创建类,关键字之后是变量标识符,最后是一个称作类主体的代码块。这种写法称作类的声明。没有使用extends关键字的类声明被称作基类:

"use strict";

// Food 是一个基类
class Food {

    constructor (name, protein, carbs, fat) {
        this.name = name;
        this.protein = protein;
        this.carbs = carbs;
        this.fat = fat;
    }

    toString () {
        return `${this.name} | ${this.protein}g P :: ${this.carbs}g C :: ${this.fat}g F`
    }

    print () {
        console.log( this.toString() );
    }
}

const chicken_breast = new Food('Chicken Breast', 26, 0, 3.5);

chicken_breast.print(); // 'Chicken Breast | 26g P :: 0g C :: 3.5g F'
console.log(chicken_breast.protein); // 26 (LINE A)

需要注意到以下事情:

类有一个独有的特性,就是 contructor 构造方法。在构造方法中我们可以初始化对象的属性。
构造方法的定义并不是必须的。如果不写构造方法,引擎会为我们插入一个空的构造方法

"use strict";

class NoConstructor {
    /* JavaScript 会插入这样的代码:
     constructor () { }
    */
}

const nemo = new NoConstructor(); // 能工作,但没啥意思

将一个类赋值给一个变量的形式叫类表达式,这种写法可以替代上面的语法形式:

"use strict";

// 这是一个匿名类表达式,在类主体中我们不能通过名称引用它
const Food = class {
    // 和上面一样的类定义……
}

// 这是一个命名类表达式,在类主体中我们可以通过名称引用它
const Food = class FoodClass {
    // 和上面一样的类定义……

    //  添加一个新方法,证明我们可以通过内部名称引用 FoodClass……        
    printMacronutrients () {
        console.log(`${FoodClass.name} | ${FoodClass.protein} g P :: ${FoodClass.carbs} g C :: ${FoodClass.fat} g F`)
    }
}

const chicken_breast = new Food('Chicken Breast', 26, 0, 3.5);
chicken_breast.printMacronutrients(); // 'Chicken Breast | 26g P :: 0g C :: 3.5g F'

// 但是不能在外部引用
try {
    console.log(FoodClass.protein); // 引用错误
} catch (err) {
    // pass
}

这一行为与匿名函数与命名函数表达式很类似。

使用extends创建子类以及使用super调用

使用extends创建的类被称作子类,或派生类。这一用法简单明了,我们直接在上面的例子中构建:

"use strict";

// FatFreeFood 是一个派生类
class FatFreeFood extends Food {

    constructor (name, protein, carbs) {
        super(name, protein, carbs, 0);
    }

    print () {
        super.print();
        console.log(`Would you look at that -- ${this.name} has no fat!`);
    }

}

const fat_free_yogurt = new FatFreeFood('Greek Yogurt', 16, 12);
fat_free_yogurt.print(); // 'Greek Yogurt | 26g P :: 16g C :: 0g F  /  Would you look at that -- Greek Yogurt has no fat!'

派生类拥有我们上文讨论的一切有关基类的特性,另外还有如下几点新特点:

上一篇下一篇

猜你喜欢

热点阅读