ES6 Class类

ES6中class是基于原型的继承的语法糖,提供更加清晰的语法来创建对象和原型。

声明和定义

es5 声明一个构造函数:

function Student(name, age) {
    this.name = name;
    this.age = age;
}

Student.prototype.getInfo = function() {
    return '{name:' + this.name + ',age:' + this.age + '}';
}

Student.prototype.setInfo = function(name, age) {
    this.name = name;
    this.age = age;
}

var p = new Student('nico', 1);
p.getInfo(); //{name:nico,age:1}
p.setInfo('siip', 10);
p.getInfo(); //{name:siip,10}

es6 声明一个类:

class Student {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getInfo() {
        return `{name:${this.name},age:${this.age}}`;
    }
    setInfo(name, age) {
        this.name = name;
        this.age = age;
    }
}
let p = new Student('nico', 1);
p.setInfo('siip', 10);
p.getInfo();//{name:siip,10}

以上两者相比之下,很可以看出,es6类函数比es5构造函数,代码量更少,并且结构层次更加简洁明了。

有些地方需要注意的是:

  1. es5存在变量提升(var xxx,function xxx(){})
  2. es6不存在变量提升(const,let,class)

Class 方法

constructor

constructor是默认的方法,就算在声明类函数时没有写constructor方法,在实例化时会自动添加上一个空的constructor

class Student {
    constructor() {
        this.desc = 'test';
    }
}

通过babel转码:

   function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var Student = function Student() {
        _classCallCheck(this, Student);//类不能直接被调用,只能通过new实例化

        this.desc = 'test';
    };

constructor方法指向自身,在一个类中只能有一个名为 “constructor” 的特殊方法。

Student === Student.prototype.constructor;//true

static

简述:顾名思义这是一个静态方法,就是不需要实例化类就可以调用的方法, 实例化对象不能继承静态方法。

class Student {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    static ageFilter(...args) {
        let stuAge = args;
        let arr = [];

        for (let i = 0; i < stuAge.length; i++) {
            if (stuAge[i].age > 12) {
                arr.push(stuAge[i])
            }
        }
        return arr;
    }
    getInfo() {
        return `{name:${this.name},age:${this.age}}`;
    }
    setInfo(name, age) {
        this.name = name;
        this.age = age;
    }
}
Student.ageFilter({
    name: 'a',
    age: 1
}, {
    name: 'b',
    age: 14
});//{name: "a", age: 14}

静态函数的this指向类函数自身,当this没有指明被谁调用时,this为undefined

我们将上面代码用babel编译,从结果上看:

    var _createClass = function() {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }
        return function(Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();

从代码可以看出,静态方法与类函数方法的区别是:当用Object.defineProperty去定义对象属性时传入的对象不同,一个传入的是构造函数的原型,另一个是传入构造函数。

es6当前还不支持静态属性

extends

关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类
class Student {
    constructor(name = null, age = null) {
        this.name = name;
        this.age = age;
    }
    getInfo() {
        return `{name:${this.name},age:${this.age}}`;
    }
    setInfo(name, age) {
        this.name = name;
        this.age = age;
    }
}

class Citizen extends Student {
    constructor(name, age) {
        super(name, age);
        this.name = 123;
    }
}
let stu = new Citizen('siip', '25');
stu.getInfo(); //{name:siip,age:25}

可以看到子类可以调用父类上的方法和属性,当使用extends继承父类时,子类constructor和super和被默认添加上,并且在构造函数内,只有调用了super函数后才能使用this,否则会抛出ReferenceError错误

     constructor() {
        this.name = 'xxx'
         super()
      }//ReferenceError

super

  • super可作为一个函数使用
  • super可作为一个对象使用

下面一个子类继承父类的Math的方法:

function MathFns() {
    this.status = 'Father'
}
MathFns.prototype = Math;

class Max extends MathFns {
    constructor() {
        super();
    }
    static base() {
        return super.max();
    }
}
let m = new MathFns();
m; //{status: "Father"}
m.max(1, 2);//2

我们可以通过babel编译,尝试去理解es6的继承,

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });

    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;

}

这个封装继承函数,主要实现的功能是,子类原型指向父类,原型构造器指向自身,然后再通过隐式原型链指向父类函数,这样子类被实例化后的对像就能使用父类原型链上的方法以及通过隐式原型链访问到父类的属性。

总结

总的来看,es6的class类实际上是基于原型链和继承做的一层封装,它的结构层次相对于es5原型链写法更加清晰明了,代码量更少。class类主要内容是构造函数、静态方法、继承。

  1. 构造函数

    • 一个类里,必有一个constructor函数,默认。
    • 类不存在变量提升
    • 一个类里只能有一个constructor函数
    • constructor函数指向类自身
  2. 静态方法

    • 静态函数的this指向类函数自身,当this没有指明被谁调用时,this为undefined
    • 在同一个类中的一个静态方法调用另一个静态方法
    • 类实例化对象不能调用类静态方法
  3. 继承

    • 子类继承父类的属性和方法
    • 在子类继承父类,子类的构造函数中要先调用super函数,才能使用this
    • super可以作为函数或对象调用,但指向都是子类

NicolaChin
35 声望2 粉丝

互相交流和提高