理解 es6 --- 类 class

本文深入探讨了ES6中类的概念及其实现细节,包括类的特点、继承机制、静态方法、生成器方法、符号物种(Symbol.species)及new.target的使用。

英文电子书点此阅读《understanding es6》

目录

class in javascript (类)

class 的特点
class PersonClass{

    constructor(name){
        this.name  = name
    }

    sayName(){
        console.log(this.name)
    }
}

let person = new PersonClass('lolita')

class 与 custom type (自定义类型)之间的差异在于:
- class 声明不能提升。类似于let, 在声明前会处于 时间死亡区域 (temporal dead zone)
- class 内部的代码 均为 严格模式。
- 所有class 中的method都不可枚举。
- 必须用 new 来调用 class
- 不能在 class 内部 重新命名 class 的名字。

class PersonClass 等价于如下代码

let PersonType2 = (function(){

    'use strict'

    const PersonType2 = function(name){

        if(typeof new.target === 'undefined'){

            throw new Error('constructor must be called with new')
        }

        this.name = name 
    }

    Object.defineProperty(PersonType2.prototype, 'sayName', {

        value: function(){

            if(typeof new.target !== 'undefined'){

                throw new Error('method cannot be called with new')
            }

            console.log(this.name)
        },

        enumerable: false,

        writable: true,

        configurable: true
    })

    return PersonType2
}())
在类中定义generator
class Collection{

    constructor(arr){
        this.items = arr
    }

    *[Symbol.iterator](){
        yield *this.items.entries()
    }
}

var collection = new Collection([1,3,4,5])

for(let a of collection){
    console.log(a)
}
static 关键词
  • 只能用className来直接获取,不能在实例上操作。
class PersonClass(){

    ...
    static create(name){
        return new PersonClass(name)
    }
}

let person = PersonClass.create('nicholas')

// 可以加在 class 中的任何 method 上,除了 constructor
扩展类 derived classes
class Rectangle {

    constructor(length,width){
        this.length = length
        this.width = width
    }

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

    static create(length, width) {
        return new Rectangle(length, width);
    }
}


class Square extends Rectangle {

    constructor(length){

        // same as Rectangle.call(this, length, length)
        super(length,length)
    }

    getArea(){
        return super.getArea()
    }
}

var square = new Square(3)

var r = Square.create(4,5)

console.log( r instanceof Square )  //false
  • extends 的类需要在 constructor 中使用 上一级的 方法,super ….., super 是关键词
  • 如果不写 constructor, 则会自动代入所有创建实例用的参数
  • 只要一个函数有[[constructor]] 和 原型,那么无论是以class 方式去写,还是以 es5的方式去写,都可以去 extends
  • 可以动态指定 extends
  • null 和 generator 函数不可以被 extends
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;
}

class Square extends mixin(AreaMixin, SerializableMixin) {
    constructor(length) {
        super();  // 必须先使用super再去取 this 的值
        this.length = length;
        this.width = length;
    }
}

var x = new Square(3);
console.log(x.getArea());               // 9
console.log(x.serialize());             // "{"length":3,"width":3}"
从built-in的类型中继承

之前会用apply的方式继承

function MyArray(){
    Array.apply(this,arguments)
}

MyArray.prototype = Object.create(Array.prototype,{
    constructor:{
        value: MyArray,
        writable: true,
        configurable: true,
        enumerable: true
    }
})

但这样的继承,this的值最先从衍生类中创建,然后原始constructor被调用(Array.apply)。也就是说, this 先作为 MyArray 的实例,然后再被 Array 的特性润饰。

而 ES6 中的 extends, this 的值先由 Array 创建,然后被 MyArray 修改。

this starts with all the built-in functionality of the base and correctly receives all functionality related to it.
class MyArray extends Array{

}
Symbol.species

对于从 built-ins 中继承来的衍生类而言,任何方法返回built-in 实例的方法都会自动转成返回 该衍生类的实例。以上面的 MyArray 为例:

let items = new MyArray(1,2,3,4), subitems = items.slice(1,2)

items instanceof MyArray // true
subitems instanof MyArray // true

这是由 [Symbol.species] 属性决定的。这是一个静态的 accessor property。返回一个constructor函数。当一个实例通过实例方法创建的时候(而不是 new 出来的实例),会被调用。

它只有 get 而没有 set。

class MyClass{
    static get [Symbol.species](){
    return this
    }

    constuctor(value){
        this.value = value
    }

    clone(){
        return new this.constructor[Symbol.species](this.value)
    }
}

class MyDerivedClass1 extends MyClass{

}

class MyDerivedClass2 extends MyClass{
    static get [Symbol.species](){
        return MyClass
    }
}

let ins1 = new MyDerivedClass1('foo'), clone1 = ins1.clone()
let ins2 = new MyDerivedClass2('bar'), clone2 = ins2.clone()

clone1 instanceof MyClass //true
clone1 instanceof MyDerivedClass1 //true
clone2 instanceof MyClass // true
clone2 instanceof MyDerivedClass2 //false


// 可以这么修改 MyArray
class MyArray extends Array{
    static get [Symbol.species](){
        return Array
    }
}
在 class constructor 中使用 new.target
class Shape{
    constructor(){
        if(new.target === Shape){
            throw new Error('this class is abstract and cannot be instantiated directly')
        }
    }
}

class Rectangle extends Shape{
    constructor(length, width){
        super()
        this.length = length
        this.width = width
    }
}

var x = new Shape()  // throws error

var y = new Rectangle(3,4)  // no error

console.log(y instanceof Shape)  // true

// 由于 class 只能用 new 来调用,因此 new.target 属性永远不会是 undefined
summary
  • ES6的类 更像是 语法糖,但加入了很多别的特性 来避免错误。
  • 通过 在 class 原型上加入非静态的 方法来继承,而静态的方法直接作用于 constructor 本身。
  • 所有的方法都不可枚举,这跟 built-ins 的对象更为贴近。
  • class 必须用 new 来调用。
  • 可以从 class, function 或者 expression 中衍生出别的类。可以用mixin 和别的组成形式来创建新的 class。
  • 可以从 built-ins中 衍生新的类,并且让他们像 想要的那样。
  • new.target 可以被用在 class constructor 中to make it behave differently depending on how the class is called. 尤其是用于创建一个抽象类。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值