语法

  1. class User{
  2. gender = '男';//实例的属性设置的新方法,因为与方法在同一级,因此不需要this
  3. constructor(name){//小括号用来定义传入参数
  4. this.name = name;//设置实例的属性
  5. }
  6. get(){console.log(this.name)}//不需要function关键字,不需要逗号或者分号,方法可以调用传入的参数
  7. changeGender(value){//当然可以通过方法来通过传入参数改变静态属性
  8. this.gender = value;
  9. }
  10. }
  11. let user1 = new User();

语法理解:

  1. 属性名可以用表达式求得
  2. 类的方法都定义在类的prototype中,这与之前的构造函数是相同的
  3. 类的内部的所有定义的方法都是不可枚举的(non-enumerable),因为他的descriptor设置了enumerable:false,使用Object.keys()或者for…in循环无法遍历,与构造函数不同。使用in关键字是可以检测的。也可以通过Object.getOwnPropertyNames()方法来查看。obj.hasOwnProperty()的返回值为true。
  4. 类的constructor方法为默认方法,必须通过new命令生成对象,new命令会默认调用constructor方法、
  5. 与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上,可以用Object.keys()遍历),否则都是定义在原型上(即定义在class上),所有实例共享一个原型,因此可以通过Object.getPrototypeof()来得到原型,再添加属性、方法,不推荐使用proto。注意,使用实例的proto来修改属性和方法会影响类的prototype,因此会影响类的所有实例,一定要谨慎!!
  6. class内部默认使用严格模式,因此全局环境下的this指向的undefined。

    class表达式

    与函数一样,class也可以用表达式定义 ```javascript const MyClass = class Me { getClassName() { return Me.name; } }; let inst = new MyClass(); inst.getClassName() // Me Me.name // ReferenceError: Me is not defined
  1. 注意 Me是类的名字,但是只能在class的内部可以使用,在外部只能用MyClass来引用,内部不需要用到me时可以省略。
  2. <a name="kk0HA"></a>
  3. # getter和setter
  4. 关于描述符的介绍,查看[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0)
  5. 1. 在类中可以使用get.set关键字对某个属性设置gettersetter函数,类比于DOM操作,相当于是监听了该属性的访问、set事件。
  6. 1. get.set设置的属性在类的prototype
  7. 1. get.set的取值函数和存值函数在属性的描述对象上,可以用Object.getOwnPropertyDescriptor( obj,'属性名')取得。
  8. 1. 通过类生成实例后,get.set的取值函数和存值函数同样在属性的描述对象上,但是这个属性同时在实例中、实例的原型__proto__(类的原型prototype)中
  9. ```javascript
  10. class User {
  11. gender = "男";
  12. constructor(name) {
  13. this.name = name;
  14. }
  15. get prop() {
  16. console.log("正在查询");
  17. }
  18. set prop(value) {
  19. console.log("设置值为" + value);
  20. }
  21. }
  22. let user1 = new User("ming");
  23. console.dir(
  24. "get" in Object.getOwnPropertyDescriptor(user1.__proto__, "prop")//true
  25. );
  26. console.dir("prop" in user1);//true
  27. console.dir("prop" in user1.__proto__);//true

静态属性和方法

1.在函数上设置静态方法时,是把函数当成对象,直接设置属性方法(或者在proto上设置)。
2.在类中,在属性和方法的前面加上static关键字,会将属性和方法转化为静态属性和静态方法,静态属性和方法不能在实例中调用,只能以类似Object.keys()这样类的方法形式调用。
3.父类的静态属性和方法可以被子类继承。
注意:静态属性和方法中的this指向类而不是实例。

保护属性的几种方法

1.一种方法是使用不同的命名来防止外部访问,例如_name=name,这样外部不能使用name来访问,而内部使用_name来编写程序。但是这样是不够安全的。
2.使用Symbol()类型的数据来保护属性。

 let protect = Symbol();
      class User {
        constructor(name, age) {
          this[protect] = {};//使用symbol声明一个对象整合属性
          this[protect].name = name;
          this[protect].age = age;
        }
      }
      class user extends User {}
      let user1 = new User("xiaoming", "18");
      let user2 = new user("xiaohua ", "19");
      console.log(user1);
      console.log(user2);
      console.log(user1.name)//undefined

3.使用weakmap数据类型来存放受保护属性。

私有属性和方法(private)

只有类的内部可以使用,外部使用会报错。
在属性和方法前加上#,将属性和方法转化成私有属性和方法,只能在类和实例的内部引用,外部不能调用。

类的继承

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

使用extends关键字来继承

类的原型

子类的proto指向父类,
子类的prototype的proto指向父类的prototype,表示方法的继承

Object.getPrototypeOf检测继承

class Parent {}
      class Child extends Parent {}
      console.log(Object.getPrototypeOf(Child) === Parent);//true

getPrototypeOf用来检测原型(proto
子类的proto指向父类,因此可以检测继承。

super关键字

作为函数

继承的时候必须在子类的constructor(只能是构造函数中,别的地方不行)中使用一次super(),这是代表父级的类的constructor。
注意:子类中的super虽然代表的是父类的constructor,但是返回的是子类的实例。

作为对象

普通方法

作为对象调用时,super指向的时父类的prototype,所以定义在父类的实例的方法或者属性是获取不到的。

lass A {
  constructor() {
    this.p = 2;
  }
}
class B extends A {
  get m() {
    return super.p;
  }
}
let b = new B();
b.m // undefined

注意:虽然super指向的是父级的prototype,但是子类普通方法中的super的this是指向子类的实例。

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();//这里指向A中的print,虽然this看起来是A中的print,但其实指向子类的实例,即2
  }
}

let b = new B();
b.m() // 2

静态方法

静态方法中,super指向的是父类本身,而不是父类的prototype

class Parent {
        static a() {
          console.log("a");
        }
        b() {
          console.log("b");
        }
      }
      class Child extends Parent {
        static c() {
          super.a();
        }
        static d() {
          super.b();
        }
      }
      let v = new Parent();
      Child.c(); //a
      Child.d(); //报错

同样:静态方法中的super中的this指向的是子类,而不是父类。

class Parent {
        static b = "这是父类的static";
        static a() {
          console.log(this.b);
        }
      }
      class Child extends Parent {
        static b = "这是子类的static";
        static a() {
          super.a();
        }
      }
      console.log(Child.a());//这是子类的static