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);

console.log(p instanceof Point)

ES6 之前模拟类的写法, 如上为方法 Point 形成构造函数. 通过 new Point() 完成创建新的 Point 实例. Point 类的通用方法, 挂于 prototype 上.

特别注意实例是无法直接访问到类的 prototype 的, 只能通过 proto 访问

ES6 的类是 ES5 的语法糖, 改变了写法, 但是大部分功能 ES5 都可实现. 如下所示

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

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

const p = new Point()

console.log(p instanceof Point) // true
console.log(typeof Point) // function
console.log(Point === Point.prototype.constructor) // true

ES6 的 class 写法中, 使用 constructor 代替了先前的 function 式构造函数. 并将需要的方法直接写在 class 内部. this 指向实例对象.

class Point 实际上 type 还是 function 并且 等价于 Point.prototype.constructor

类中定义的所有方法都是绑定在 prototype 上, 这一点与 ES6 之前的做法一致, 只是一种语法糖, 因此实例方法调用上, 本质上调用的是原型链上的方法

class Point {
}

// 等同于
class Point {
  constructor() {}
}

在类中, constructor 方法是必须的, 没有的话, JavaScript 引擎会自动为它添加一个空的constructor()方法.

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

//定义类
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

值得注意的一点, 类中定义的属性, 除非显示定义在实例 this 上的, 其余都是定义在类的原型上.

proto 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,不建议在生产环境中使用, 生产环境中,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型


实例属性的新写法

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

实例属性 this._count 定义在 constructor()方 法里面。另一种写法是,这个属性也可以定义在类的最顶层,其他都不变。


静态方法/静态属性

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

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

静态方法包含this关键字,这个this指的是类,而不是实例。

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

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

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

父类的静态方法,可以被子类继承。

静态方法也是可以从super对象上调用的。

class Foo {
}

Foo.prop = 1;
Foo.prop // 1

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

新提案可以在为 class 内部的属性前加入 static, 就可以变成静态属性了


私有方法和私有属性


注意点

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

需要明确区分实例与类的区别

class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]


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

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

Object.keys(Point2.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point2.prototype)
// ["constructor","toString"]

在 ES6 中, 类中定义的方法是不可枚举的, 然而直接添加在原型链上的方法确是可以枚举的





以往
JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。