javascript--类

237 阅读3分钟

定义一个类的一种方法是使用一个类声明。要声明一个类,你可以使用带有class关键字的类名(这里是“Rectangle”)。

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

函数声明类声明之间的一个重要区别是函数声明会提升,类声明不会。


原型方法

class Rectangle {
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
    calcArea() {
        return this.height * this.width;
    }
}
const square = new Rectangle(10, 10);


只能在constructor里构造属性,在constructor外面构造函数,而且constructor里构造的属性会出现在实例上而constructor外面的会出现在实例原型上

静态方法

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

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

Foo.classMethod() // 'hello'

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

实例属性的新写法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。这时,不需要在实例属性前面加上this

class IncreasingCounter {
  constructor() {
    this._count = 0;
  }
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

对比

class IncreasingCounter {
  _count = 0;
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

静态属性

静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

// 老写法
class Foo {
  // ...
}
Foo.prop = 1;

// 新写法
class Foo {
  static prop = 1;
}

老写法的静态属性定义在类的外部。整个类生成以后,再生成静态属性。

Class 的继承

Class 可以通过extends关键字实现继承,

class Point {
}

class ColorPoint extends Point {
}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

意思是super()方法回调用父类的constructor但是this指的是子类的实例

原型链思想

类本质上是一个函数(构造函数),类的所有方法都定义在类的prototype属性上面。即实例的__proto__上面,构造函数的prototype属性,在 ES6 的“类”上面继续存在。

继承机制

ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。实例对象同时是ColorPointPoint两个类的实例,这与 ES5 的行为完全一致。

super

第一种情况,作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错,作用是调用父类的constructor,但是父类的this指向子类的实例

第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。super === A.prototype

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

类的 prototype 属性和__proto__属性

大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
 A.prototype //里面都是A的方法

实例的 __proto__ 属性 

var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');

p2.__proto__.__proto__ === p1.__proto__ // true
B.prototype.__proto__ === A.prototype // true
两个道理是一样的