理解 es6 --- 类 class

英文电子书点此阅读《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. 尤其是用于创建一个抽象类。

猜你喜欢

转载自blog.csdn.net/github_36487770/article/details/79725542