ES6新特性 class(类定义和使用)

电玩女神 2022-02-20 07:16 405阅读 0赞

intro

在ES6中,引入了class关键字用于快速地定义“类”。
在JS中,“类”的本质是function
可以将其看做一个语法糖,让对象原型的写法更简洁清晰,更像面向对象编程的语法。

涉及关键字:

  1. class 声明类模板
  2. constructor 构造方法
  3. get getter方法
  4. set setter方法
  5. static 静态成员
  6. extends 继承
  7. super 在子类中表示父类对象

类定义

  • 常规
    类名不能重复定义。
    class的本质是函数function

    class Person {
    constructor(a) {

    1. this.a = a;

    }
    }

    console.log(typeof Person); // function

    // class Person {} // 报错:不能重复定义
    // Uncaught SyntaxError: Identifier ‘Person’ has already been declared

  • 类表达式
    将类定义部分作为一个表达式,赋值(其地址的值)给一个变量。
    类表达式中的类名 可省略可重复
    创建对象时,使用的是接受类表达式(地址)的变量名 充当类名。
    如果使用类表达式中的类名创建对象,会报错。

    var p = class Person {}; // 有名类表达式
    new p(); // Person {}
    // new Person(); // Uncaught ReferenceError: Person is not defined

    var p = class {}; // 匿名类表达式

    var a = class AAA {};
    var b = class AAA {}; // 不报错
    console.log(a == b); // false, a和b是两个不同的类(类声明)。

  • 类定义不会提升(hoisting)
    使用class简洁地定义类,应该先定义,后使用。否则报错。

  • 类中的方法

    • 方法不能使用function关键字。
    • 方法之间使用分隔符;,可省略(会自动补全)。
    • 原型变量之间的分隔符也是;

静态成员和原型成员

ES6中,虽然新增了class关键字用于类定义,但其实质还是function,只不过是为了方便书写,随后经过了某种源代码编译过程。

prototype仍然存在。在类体中定义的方法还是定义在prototype上。

class Person {}
类 :Person
原型:Person.prototype

  • 原型成员
    static关键字的成员。
    this.xxx
    类名.prototype.xxx

    1. class Person {
    2. // xxx
    3. name = "无";
    4. eat() { // 原型方法 推荐写法
    5. console.log("eat func");
    6. };
    7. // this.xxx 打印对象时会打印出来。
    8. constructor(a) {
    9. this.age = a; // 原型属性 推荐写法
    10. this.drink = function() {
    11. console.log("drink func");
    12. };
    13. };
    14. }
    15. // 类名.prototype.xxx 打印对象时不会打印。但可以访问到。
    16. Person.prototype.gender = "男"; // 不推荐
    17. Person.prototype.foo = function() { // 可以
    18. console.log("foo func");
    19. };
    20. var a = new Person(22);
    21. a; // Person {name: "无", age: 22, drink: ƒ}
    22. Person.prototype; // {gender: "男", foo: ƒ, constructor: ƒ, eat: ƒ}

    推荐:

    • 原型属性就在constructor中用this.xxx即可。
    • 原型方法推荐写在类体中,也可以手动添加到类名.prototype对象中。这两种都行。
    • 统一规则,尽量避免出现其他写法。
  • 静态成员
    带关键字static
    类名.xxx

    ES6中规定,class内部只有静态方法,没有静态属性。
    但是在新提案中,有静态属性。
    现阶段:

    • 构造方法constructor()中不能调用静态成员(方法,属性)
    • 静态方法中不能调用静态属性(不论类体内,还是外部)。
    • 否则报错: xxx is not defined

      class Person {
      stativ version = “1.0”; // 不推荐,并非ES6中实现的标准
      static sum(a, b) {return a + b;}; // 静态方法 推荐写法
      constructor() {}
      }
      Person.desc = “人类”; // 静态属性 推荐写法
      Person.sub = function(a, b) {return a - b;};

    因为版本实现问题,推荐写法:

    • 静态属性,一定在类体外用类名.xxx = value;定义。
    • 静态方法写在类体内部用static声明,或写在外部也可以(两种都行)。
      NOTE
    • 构造方法中不得访问静态成员。
    • 静态成员之间不得互相调用(静态方法内访问静态属性,静态方法互相调用)
  • 向对象添加成员(成员方法,成员属性)
    类名添加成员,就相当于静态成员。
    类名.prototype添加成员,就相当于原型成员。

有两种方法:
obj.key = value;
Object.assign(target, ...sources);

常见方法

  • constructor
    constructor方法是类的默认构造方法,创建类对象的时候被调用。
    可以在constructor方法中返回指定的类型对象(可与本类不相同)。

    class One {
    constructor() {

    1. console.log("One constructor()");
    2. // return this; // 默认返回this实例对象

    }
    }

    class Two {
    constructor() {

    1. return new One();

    }
    }

    var a = new Two(); // One constructor()
    console.log(a instanceof Two); // false
    console.log(a instanceof One); // true

可见,使用new操作符实例化出的对象,实质是什么类型,取决于其构造方法constructor()的返回值。

  • getters, setters

    • 关键字setget,置于方法名前(如get name() {...}set name() {...})。
    • setter, getter中访问成员属性的格式:this._xxx而非this.xxx
    • 任何读操作都会调用get方法
    • 任何写操作都会调用set方法

      • 构造方法内部this.prop = value;
      • 类体外部obj.prop = value;
    • getter, setter不可单独出现

      • 只有setter,不能通过obj.xxx读取xxx的值。但可以通过打印obj查看xxx的值。
      • 只有getter

        • 如果constructor()中有对应的this.xxx=value;的语句,则 因为没有setter,所以实例化时会报错。
        • 如果constructor()中没有this.xxx=value;的语句。则实例化时不报错。
          但是,没有意义(这个属性不set值,用不了,只能getundefined的值)。
    • getter, setter必须同级出现(继承关系中)。

    class Person {
    constructor(age) {

    1. this.age = age;

    };
    get age() {

    1. console.log("get age()");
    2. return this._age; // 注意是this._age

    };
    set age(age) {

    1. console.log("set age()");
    2. this._age = age; // 注意是this._age

    }
    }

    var p = new Person(22); // set age()
    console.log(p); // Person {_age: 22}
    console.log(p.age); // 22
    console.log(p._age); // 22

getter, setter中调用原型属性:this._xxx
而非this.xxx,否则会在实例化对象(调用构造方法时)报错RangeError

  • decorator
    decorator是一个函数,用来修改类的行为,在代码编译时产生作用。
    decorator方法不是ES6标准,是ES7的新提案之一。
    后期再深入。

extends

关键字:extends, super

  • super

    • super表示父类。
    • super(...argsList)表示父类的构造方法
    • super.xxx可以访问父类的成员属性
    • super.xxx()可以访问父类的成员方法
  • 在子类的构造犯法中访问this或返回返回值之前,必须先调用父类的构造方法super()
    Must call super constructor in derived class before accessing 'this' or returning from derived constructor

    class Father {
    constructor(firstName) {

    1. console.log("father...");
    2. this.firstName = firstName;

    }
    }
    class Child extends Father {
    constructor(firstName, lastName) {

    1. super(firstName);
    2. console.log("child...");
    3. this.lastName = lastName;

    }
    }

    Father.prototype.isPrototypeOf(Child.prototype); // true

    var a = new Child(“J”, “DD”);
    // father…
    // child…
    // Child {firstName: “J”, lastName: “DD”}

  • 子类中调用父类的方法

    • 原型方法:在子类的原型方法中super.xxx(),其中xxx为原型方法名。
    • 静态方法:在子类的静态方法中super.xxx()父类名.xxx(),其中xxx为静态方法名。

    class Father {
    foo() {console.log(“foo…”);}
    static bar() {console.log(“bar…”)}
    }

    class Child extends Father {
    f() {

    1. super.foo(); // 在子类的原型方法中调用父类的原型方法

    }
    static b() {

    1. super.bar(); // 在子类的静态方法中调用父类的原型方法
    2. Father.bar();

    }
    }

    Child.b() // 用类名调用静态方法
    // bar…
    // bar…
    (new Child()).f() // 用对象调用原型方法
    // foo…

即:
父类的构造方法可以在子类的构造方法中被调用。
父类的原型方法可以在子类的原型方法中被调用。
父类的静态方法可以再子类的静态方法中被调用。
否则出错。

  • 不可以继承对象字面量
    var obj = {name:"JT", showName:function() {console.log(this.name)}};
    如果想定义一个类Child,也拥有这个对象的成员。
  1. 直接继承对象字面量 - 不可以,报错。
    Uncaught TypeError: Class extends value #<Object> is not a constructor or null

    var Father = {name:”JT”};
    class Child extends Father {};

  2. 操作原型链

    var Father = {name:”JT”};
    class Child {};
    Object.setPrototypeOf(Child.prototype, Father);

    (new Child()).name; // “JT”

发表评论

表情:
评论列表 (有 0 条评论,405人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ES6特性

    1.变量声明let和const 在ES6以前,var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。这就是函数变量提升例如:

    相关 ES6特性

    1.声明变量的关键字:const 和 let JavaScript ES6中引入了另外两个声明变量的关键字:const和let。在ES6中,我们将很少能看到var了。 co