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属性的。

相关推荐
顾安r3 分钟前
11.22 脚本打包APP 排错指南
linux·服务器·开发语言·前端·flask
万邦科技Lafite9 分钟前
1688图片搜索商品API接口(item_search_img)使用指南
java·前端·数据库·开放api·电商开放平台
czhc114007566314 分钟前
c# 1121 构造方法
java·javascript·c#
yinuo1 小时前
网页也懂黑夜与白天:系统主题自动切换
前端
Coding_Doggy2 小时前
链盾shieldchain | 项目管理、DID操作、DID密钥更新消息定时提醒
java·服务器·前端
用户21411832636022 小时前
dify案例分享-国内首发!手把手教你用Dify调用Nano Banana2AI画图
前端
wa的一声哭了2 小时前
Webase部署Webase-Web在合约IDE页面一直转圈
linux·运维·服务器·前端·python·区块链·ssh
GISer_Jing2 小时前
3D Cesium渲染架剖析
javascript·3d·webgl
han_2 小时前
前端性能优化之CSS篇
前端·javascript·性能优化
k***85842 小时前
【SpringBoot】【log】 自定义logback日志配置
android·前端·后端