浅谈ES6类

ECMAScript6中终于引入了类的特性,在此之前只能通过其他方法定义并关联多个相似的对象,当然了,ES6中的类与其他语言中的还是不太一样,其语法的设计实际上借鉴了JavaScript的动态性,本文档简单介绍一下ES6及其新特性。

类的声明

ES6中声明一个类,首先编写class关键字,紧跟着是类的名字,其他部分的语法类似于对象字面量方法的简写形式,但是不需要子类的各元素之间使用逗号分隔,请看下面这段简单的类声明代码:

    class PersonClass{
        constructor(name){
            this.name = name
        }
        sayName(){
            return this.name
        }
    }

    let person = new PersonClass('xiaoMing')
    console.log(person.sayName())                        // 'xiaoMing'
    console.log(person instanceof PersonType)           //true
    console.log(person instanceof Object)              //true

    console.log(typeof PersonClass)                    // function
    console.log(typeof PersonClass.prototype.sayName) // function

对比类声明语法定义PersonClass的行为与ES5之前用构造函数构建近似类过程相似,但是仍有很多差异:

1.  函数声明可以被提升,而类声明与let声明类似,不能被提升;真正执行声明语句之前,他们会一直存在于临时死区中。  
2.  类声明中的所有代码将自动运行在严格模式下,而且无法强行让代码脱离严格模式执行。
3.  在自定义类型,需要通过Object.defineProperty()方法手工指定某个方法为不可枚举,而在类中,所有的方法都是不可枚举的。
4.  每个类都有一个名为[Constructor]]的内部方法,通过关键字new调用方那些不含[Constructor]]的方法会导致程序抛出错误。
5.  使用关键字new以外的方式调用类的构造函数会导致程序抛出错误。
6.  在类中修改类名会导致程序报错。

类不仅仅可以通过声明实现,还可以使用类表达式 ,类表达式又分为匿名类表达式和命名类表达式,具体语法如下所示:

//匿名类表达式
    let PersonClass = class{
        constructor(name){
            this.name = name
        }
        sayName(){
            console.log(this.name)
        }
    }
//命名表达式
    let PersonClass = class PersonClass2{
        constructor(name){
            this.name = name
        }
        sayName(){
            console.log(this.name)
        }
    }
    console.log(typeof PersonClass)          // function
    console.log(typeof PersonClass2)        // undefined                

两者除了命名表达式需在关键字class后添加一个标识符外,并无其他差别,但是注意typeof PersonClass2 输出的是undefined,这是因为PersonClass2只存在于类定义中,而在类的外部,其实并不存在一个名为PersonClass2的绑定。下边我们用一段没有使用关键字class的等价声明来了解这背后的原理:

    let PersonClass = (function () {
        const PersonClass2 = function (name) {
            if (typeof new.target === "undefined") {      //类与构造函数差异的第五条
                throw new Error("必须通过new关键字调用构造函数")
            }
            this.name = name
        }
        Object.defineProperty(PersonClass2.prototype, 'sayName', {
            value: function () {
                if (typeof new.target !== "undefined") {    //类与构造函数差异的第四条
                    throw new Error("不可使用new关键字调用该方法")
                }
                console.log(this.name)
            },
            enumerable: false,
            writable: true,
            configurable: true,
        })
        return PersonClass2
    }())

外部作用域中的PersonClasss是用let声明的,而立即表达式中的PersonClass2是用const声明的,这也说明了为什么可以在外部修改类名而内部却不可修改,且PersonClass2只能在类内部使用。

类的特点

下边简单介绍一下类中的几个小特性

  • 一级公民类
    在编程中,能被当做值使用的就成为一级公民,意味着它可以被当做参数传给函数,可以作为函数返回值,能用来赋值给变量,JS的类也可以被当成值使用,就是一级公民类。
  • 访问属性
    自有属性需要在类构造器中创建,而类还允许你在原型上定义访问器属性。为了创建一个getter ,要使用 get 关键字,并要与后方标识符之间留出空格;创建 setter 用相同方式,只是要换用 set 关键字。
  • 需计算成员名
    类方法与类访问器属性也都能使用需计算的名称。语法相同于对象字面量中的需计算名称:无须使用标识符,而是用方括号来包裹一个表达式。
  • 生成器方法
    可以使用 Symbol.iterator 来定义生成器方法,从而定义出类的默认迭代器。
  • 静态成员
    ES6 类的静态成员的创建,只要在方法与访问器属性的名称前添加正式的 static 标注。你能在类中的任何方法与访问器属性上使用 static 关键字,唯一限制是不能将它用于 constructor 方法的定义。

继承与派生类

首先我们来看下ES6中是怎么实现继承的:

    class Rectangle(){
        constructor(length,width){
            this.length = length
            this.width = width
        }
        getArea(){
            return this.length * this.width
        }
    }

    class Square extends Rectangle {
        constructor(length){
            //等价于Rectangle.call(this,length,length)
            super(length,length)
        }
    }
    var square = new Square(3)
    console.log(square.getArea())              //9
    console.log(square instanceof Suqare)     //true
    console.log(square instanceof Rectangle) //true

ES5中Square继承自Rectangle,为了这样做,必须创建自Rectangle.prototype的新对象重写Square.prototype并调用Rectangle。call()方法,这些步骤很容易让人感到困惑,并在这里犯错。ES6类的出现让我们轻松实现了继承功能,使用extends关键字可以指定类继承的函数,继承自其他类的类被称作派生类,如果在派生类中指定了构造函数则必须要调用super(),通过调用super()方法即可访问基类的构造函数,原型会自动调整,如果不这样做,程序就会报错。如果选择不适用构造函数,则当创建新的类实例时会自动调用super()并传入所有参数。举个例子,一下两个类完全相同:

    class Square extends Rectangle{
        //没有构造函数
    }

    clas Square extends Rectangle{
        constructor(...args){
            super(...args)
        }
    }

派生类的特性

派生类除了上述的特征外,还有一些其他的特性:

  1. 派生类中的方法总会覆盖基类中的同名方法。
  2. 如果基类中有静态成员,那么这些静态成员在派生类中也可用。
  3. 只要表达式可以被解析为一个函数并且具有[[constructor]]属性和原型,就可以用extends进行派生。
  4. ES6支持内建对象的继承。

ES6 的类让 JS 中的继承变得更简单,是javaScript新特性的一个重要组成部分,这一特性提供了一种更简洁的语法和更好的功能,可以让你通过一个安全、一致的方法来自定义对象类型。

猜你喜欢

转载自www.cnblogs.com/beginner123/p/9677709.html
今日推荐