js 中的类(class)

本来打算介绍下 ts 中的类,但是转念一想还是先来说说 js 中的类吧。

定义类的方法

在 ES6 中,我们可以使用 class 关键字直接定义类。定义的方式类似于函数的定义方式,可以分为 2 种:

  1. 类声明(比较常见):
javascript 复制代码
// 例 1
class Animal {}
  1. 类的表达式:
javascript 复制代码
const Animal = class {}

小提示:如果通过 typeof 来查看例 1 中声明的类 Animal 的类型,console.log(typeof Animal) 结果会是 "function",这是因为 typeof 可能的返回值是固定的那么几个,里面没有 class。

类的特点

其实,class 定义类从本质上可以看成是 ES5 中的构造函数的语法糖,例 1 可以看成是定义了一个构造函数: function Animal() {},所以构造函数的那些特性类也有:

  • 类也有 prototype 属性;
  • 类的原型对象的 [[Prototype]] 属性默认也是指向 Object.prototype
javascript 复制代码
console.log(Animal.prototype.__proto__ === Object.prototype) // true
  • 类的原型对象也有 constructor 属性,默认指向类本身:
javascript 复制代码
console.log(Animal.prototype.constructor === Animal) // true
  • new 一个类的时候,同样会让生成的对象(最后返回作为实例对象)的隐式原型指向类的显示原型:
javascript 复制代码
const animal = new Animal()
console.log(animal.__proto__ === Animal.prototype) // true

不难发现,如果之前关于构造函数与原型、原型链那部分知识掌握好了,类的学习就会轻松许多。

类的构造方法(constructor)

在构造函数中,我们可以给函数传参,在函数中通过 this.xx = yy的形式最终让生成的实例拥有值为 yy 的 xx 属性。在类中,我们则可以通过定义构造函数的方式来实现。注意,构造函数只能定义 1 个,且函数名必须为 constructor

javascript 复制代码
// 例 2.1
class Animal {
  constructor(height, weight) {
    this.height = height
    this.weight = weight
  }
}

const animal = new Animal(2, 200)
console.log(animal) // Animal {height: 2, weight: 200}

构造函数 constructor 会在我们通过 new 创建类的实例时调用:const animal = new Animal(height, weight),即使我们像例 1 那样不自己手写定义,创建实例时还是会执行默认的constructor函数。执行构造函数时做的事情和 new 一个构造函数背后做的事情如出一辙:

  1. 在内存中创建一个新的空对象;
  2. 类的 prototype的值会被赋给第 1 步创建的对象的 [[Prototype]] 属性;
  3. 构造函数内部的 this,也会指向第 1 步创建的对象;
  4. 执行函数的内部代码(函数体);
  5. 如果构造函数没有返回非空对象,则返回第 1 步创建的对象(其实也就是 this)。

类的方法

类里面可以定义的方法分为 3 种:

1. 实例方法

实例属性的定义我们通过 constructor 函数解决了,那么实例的方法我们在类中是如何定义的呢?很简单,直接在类中定义即可,比如我们想给 Animal 类添加一个实例方法 eat

javascript 复制代码
// 例 2.2
class Animal {
  // ...
  eat() {
    console.log('我是 Animal 类的实例的方法')
  }
}

如此,eat 方法就会被添加到 Animal.prototype 上,可以打印 Animal 原型对象自身的所有属性来验证:

javascript 复制代码
console.log(Reflect.ownKeys(Animal.prototype)) // [ 'constructor', 'eat' ]

方法还有另外一种比较少见的写法如下:

javascript 复制代码
// 例 2.2.1
class Animal {
  // ...
  eat = () => {
    console.log('我是 Animal 类的实例的方法')
  }
}

注意第 4 行这里不是用 : 而是用 = 赋值的形式。

2. 访问器方法(存储器属性)

在类里面还可以通过 getter 和 setter 方法定义实例的属性,以便之后在获取或修改该属性时进行一些拦截操作:

javascript 复制代码
// 例 3
class Animal {
  constructor() {
    // ...
    this._flag = '我是个属性'
  }
  // ...
  get flag() {
    return this._flag
  }
  set flag(val) {
    this._flag = val
  }
}

如上,我们就给 Animal 的实例添加了属性 flag

javascript 复制代码
const animal = new Animal()
console.log(animal.flag) // 我是个属性

注意:

  • 虽然 flag 是个访问器方法,但其实是个属性(存储器属性),获取的时候 animal.flag 是不能加括号的。
  • 如果没有设置 set 方法,那么在 js 中,执行 animal.flag = 'xxx' 并不会报错(ts 会报错),但也不会生效。

3. 静态方法

我们可以通过 static 关键字定义静态方法。

javascript 复制代码
// 例 4.1
class Animal {
  // ...
  static getFlag() {
    console.log('我是 Animal 类的静态方法')
  }
}

不同于实例方法,静态方法直接通过类调用:Animal.getFlag(),而无需生成实例对象。比如 Date.now()now() 方法就是 Date 类的一个静态方法

静态属性

虽然 ES6 是不支持静态属性的,但我们可以通过 static 配合访问器方法来定义类的静态属性。

javascript 复制代码
// 例 4.2
class Animal {
 // ...
  static get flag() {
    return '我是 Animal 类的静态属性'
  }
}

这样我们就可以通过 Animal.flag获取到 Animal 类的静态属性 flag 了。注意和例 3 进行对比,例 4.2 中我们不能在第 5 行返回 this._flag,因为当我们通过 Animal.flag 获取 flag 时,this 指向的是 Animal,Animal 本身是没有 _flag属性的。

相关推荐
小满zs3 分钟前
Next.js第九章(AI)
前端
晨枫阳9 分钟前
不同语言的元组对比
java·前端·javascript
柒儿吖1 小时前
Electron for 鸿蒙PC 窗口问题完整解决方案
javascript·electron·harmonyos
flashlight_hi1 小时前
LeetCode 分类刷题:404. 左叶子之和
javascript·算法·leetcode
芳草萋萋鹦鹉洲哦2 小时前
【tauri+pixijs】关于unicode/ascII/GB2312
前端·tauri·pixijs
木易 士心2 小时前
th-table 中 基于双字段计算的表格列展示方案
前端·javascript·angular.js
fakaifa2 小时前
【全开源】智慧共享农场源码独立版+uniapp前端
前端·uni-app·智慧农场·源码下载·智慧农场小程序·智慧共享农场
toooooop82 小时前
uniapp多个页面监听?全局监听uni.$emit/$on
前端·javascript·uni-app
骨子里的偏爱3 小时前
【案例】uniapp实现内部信息与外部的html网页双向通信的完整的过程,附加完整的代码部分
前端·uni-app·html
爱泡脚的鸡腿3 小时前
uni-app D4 实战(小兔鲜)
前端·vue.js·架构