Class

class是es5原型链继承的语法糖,通过babel编译之后还是构造函数,es6的类在次继承上又添加了静态方法、静态属性
class不存在变量提升的问题

constructor

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加

constructor 方法默认返回实例对象,完全可以返回另外一个对象 Object.create(null)

  1. class Person {
  2. constructor(name,age){
  3. this.name = name;
  4. this.age = age;
  5. }
  6. func() {}
  7. }
  8. const p = new Person("张大炮",20);
  9. //实例化一个类依然使用new关键字,如果没有会报错
  10. // null
  11. class Foo {
  12. constructor() {
  13. return Object.create(null);
  14. }
  15. }

为一个类添加方法,此方法是实例方法,可以被实例对象访问,也可以被子类继承。
但类的内部所有定义的方法,都是不可枚举的
下面采用es5的方式,是可以枚举的

  1. class Point {
  2. constructor(x, y) {
  3. // ...
  4. }
  5. toString() {
  6. // ...
  7. }
  8. }
  9. Object.keys(Point.prototype)
  10. // []
  11. Object.getOwnPropertyNames(Point.prototype)
  12. // ["constructor","toString"]

instance

所有的实例共享一个原型对象

  1. const p1 = new Point(2,3);
  2. const p2 = new Point(3,2);
  3. p..__proto__ === p2.__proto__; // true

所以通过原型添加方法,其他实例也可以使用到该方法

  1. const p1 = new Point(2,3);
  2. p1.__proto__.print = fuction(arg) {
  3. consoel.log(arg)
  4. }
  5. const p2 = new Point(3,2);
  6. p2.print(1) // logs -> 1

由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例

extends

子类必须再constructor方法中调用super方法,否则新建实例时会报错,因为子类没有自己的this对象,而是继承父类的this对象

继承是先创造父类的实例对象this,然后再调用子类的构造函数来修改this,如果子类没有显示的定义constructor,也会被默认的添加。

子类的proto属性指向父类,表示构造函数的继承
子类的prototype属性的proto属性,指向父类的prototype属性,表示方法的继承

  1. class Person {
  2. constructor(name,age){
  3. this.name = name;
  4. this.age = age;
  5. console.log(new.target.name) //指向当前正在执行的函数
  6. }
  7. flag = true
  8. sayName(){
  9. return this.name;
  10. }
  11. static classFunc(){
  12. console.log("此乃静态方法,不会被实例所继承")
  13. }
  14. }
  15. class Child extends Person {
  16. constructor(bgk){
  17. super("name",20)
  18. this.bgk = bgk
  19. }
  20. }
  21. const p1 = new Person();
  22. const c1 = new Child();
  23. c1.__proto__ === p1.__proto__ //false
  24. c1.__proto__.__proto__ === p1.__proto__ //true 子类的原型的原型,是父类的原型。
  25. class A {
  26. }
  27. class B extends A {
  28. }
  29. B.__proto__ === A // true
  30. B.prototype.__proto__ === A.prototype // true

instance child

子类的实例的 proto_ 属性的 proto__ 属性,指向的父类实例的 proto属性。既子类的原型的原型,是父类的原型_

const p1 = new Person();
const c1 = new Child();

c1.__proto__.__proto__ === p1.__proto__; // true

this

类的方法内部如果含有this,它默认指向类的实例。
解决 :

  1. 使用bind绑定
  2. 使用箭头函数
  3. 使用proxy代理

    super

    super关键字,,既可以当作函数使用,也可以当作对象

当为函数时, 必须在子类的constructor里调用,表示父类的构造函数 ; 既super内部的this默认指向的是父类
当为对象使用时,指向的父类的原型对象。由于指向的父类的原型对象,所以父类的实例方法,super无法访问

super 在被当作对象调用时,this指向的子类

class Parent {
    constructor() {
      this.site = “上海”
  }

  fn() {
   console.log("fn")
  }
}

class Child extends Parent{
    constructor() {
      super();
    super.fn(); // logs -> fn
    super.site // logs -> undefined
  }
}

const b  = new Child();

new/target/name

new 是从构造函数生成实例的命令。
如果构造函数不是通过new 命令调用的,new.target 会返回undefiend

总是指向被执行的函数,被实例化的时候指向自己。
子类继承父类的时候,new.target 会返会子类

static

类相当于实例的原型,所有类中的方法都可以被实例继承, 一个方法或属性前加上static关键字,表示该方法不会被实例继承,只可以通过类来调用. 称之为静态方法
父类的静态方法,可以被子类继承,也可以从super对象上调用

getter/setter

  • 在Class内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

    class Get{
    
    static mso = 10
    
      get mo() {
          return Get.mso
      }
      set mo(value) {
          console.log("newValue --> %s", value);
          Get.mso = value;
      } 
    }
    const g = new Get()
    console.log(g.mo) // get this.mso
    g.mo = "新的值" // set  this.mso = "新的值"
    

Mixin

mixin 模式指的是,将多个类的接口,混入到另一个类中
~~ 举个栗子

function mix(...mixins) {
  class Mix {}

  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }

  return Mix;
}

function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor" && key !== "prototype" && key !== "name" ) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}

// 使用
class Foo extends min(Cl1, Cl2) {}

Decorator

修饰器是一个函数,用来修改类的行为
修改类的行为,是在代码编译时发生的,而不是运行时。

类的修饰

~~ 一个简单的栗子

function test(target) {
    target.Desc = "一个类的表述"
}

@test
class Foo{}

log(Foo.Desc) // -> 一个类的描述

实际应用中一个修饰器可以做很多事,当前也需要很多参数

function test(targs) {
    return function(target) {
      target.Desc = targs
  }
}

@test(`自定义描述`)
class Foo{}

log(Foo.Desc) // -> 自定义描述

方法的修饰

修饰器还可以修饰方法
属性的话可以使用getter
方法的修饰参数

  • target 修饰的目标对象
  • name 要修饰的属性名
  • descoptor 该属性的描述对象

~~ 简单的栗子

class Person {
  @nonenumerable
  get name() { return "张大炮"; }
}

function nonenumerable(target, name, descriptor) {
  descriptor.enumerable = false;
  return descriptor;
}

上面修饰符的作用修改属性为不可枚举性

多个修饰符的叠加是一层一层进行的,