class 的基础知识

163 阅读3分钟

js 中的class(构造函数的语法糖)

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

等价与

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

class 的数据类型

 typeof class =  'function'

手动修改class 的原型

Object.assign(Point.prototype, { toString() { console.log('string') }, toValue() { console.log('value') } })

覆盖原型上方法;

Object.keys(Point.prototype) == ['toValue'];

在类上定义的方法是不可枚举

var Point = function (x, y) {
 // ...
};

Point.prototype.toString = function() {
 // ...
};

Object.keys(Point.prototype) 这种直接在构造函数的原型上加方法是可以枚举的(这是和class 不一样的地方)

    Object.keys(Point.prototype) == ['toString']

constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

class Foo {
 constructor() {
   return Object.create(null);
 }
}

new Foo() instanceof Foo

class 必须通过new 调用

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

class Point {

 constructor(x, y) {
   this.x = x;
   this.y = y;
 }

 toString() {
   return '(' + this.x + ', ' + this.y + ')';
 }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

不建议使用__proto__ ,因为这是浏览器厂商自己定义的, 所有的实例共享一个原型对象

Object.getPrototypeOf(point) == point.__proto__

类的静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()

静态方法中的this 指向问题

从这个例子还可以看出,静态方法可以与非静态方法重名。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

静态方法的继承

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'

可以在子类的super对象上面上调用

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"

实例属性的旧的写法


class Foo {
    constructor() {
        this._count = 0
    }
    
    getValue() {
        return this._count
    }
}

实例属性的新的写法


class Foo {
   _count = 0;
   
    getValue() {
     return this._count
    }
}

类的静态属性

class Foo {
}

Foo.prop = 1;
Foo.prop 

Es6规定class 内部只用静态方法, 没有静态属性 提案中的静态属性的写法

class MyClass {
  static myStaticProp = 42;

  constructor() {
    console.log(MyClass.myStaticProp); // 42
  }
}

类的私有方法和私有属性的实现 一种做法是命名上加以区别, 这种方法是靠不住的, 实例上面依然是可以调用这个方法的

class Widget {

  // 公有方法
  foo (baz) {
    this._bar(baz);
  }

  // 私有方法
  _bar(baz) {
    return this.snaf = baz;
  }

  // ...
}

另一种做法,将私有方法移出模块,因为模块内部的所有方法都是对外可见的。

class Widget {
  foo (baz) {
    bar.call(this, baz);
  }

  // ...
}

function bar(baz) {
  return this.snaf = baz;
}

第三种方法,利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值,一般情况下无法获取到它们,因此达到了私有方法和私有属性的效果。但是也不是绝对不行,Reflect.ownKeys()依然可以拿到它们。

const bar = Symbol('bar');
const snaf = Symbol('snaf');

export default class myClass{

  // 公有方法
  foo(baz) {
    this[bar](baz);
  }

  // 私有方法
  [bar](baz) {
    return this[snaf] = baz;
  }

  // ...
}

const inst = new myClass();

Reflect.ownKeys(myClass.prototype)
// [ 'constructor', 'foo', Symbol(bar) ]

new Target 属性

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true

需要注意的是,子类继承父类时,new.target会返回子类。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length) {
    super(length, width);
  }
}

var obj = new Square(3); // 输出 false

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确