您的位置:首页 > Web前端 > JavaScript

[JS]类

2021-08-02 17:51 1101 查看

类的简介

类是用于创建对象的模板,这与其他面向对象编程语言的概念一样。但是,在 JavaScript 中,类实际上是特殊的函数,就像你能够定义的函数表达式和函数声明一样。

构造函数

构造函数是一种专门用于初始化新对象的函数。使用 new 关键字调用构造函数会自动创建新对象,因此构造函数本身只需要初始化新对象的属性。构造函数的 prototype 将被作为新对象的原型的属性。

function Person(name, age) {
this.name = name
this.age = age
}

Person.prototype = {
run() {
return 'run...'
},
eat() {
return 'eat...'
}
}

let xiaohong = new Person('小红', 11)
let xiaoming = new Person('小明', 10)
console.dir(Person) // 输出构造函数
console.dir(xiaohong) // 输出实例化对象

构造函数 Person() 的 prototype 被作为新对象的原型的属性。

this 关键字

在构造器初始化属性时,赋值表达式左侧是 this.name ,this 指向的是实例化对象本身。

function Person(name, age) {
this.name = name
this.age = age
console.dir(this) // 打印this
}
let xiaohong = new Person('小红', 11)
console.log(xiaohong) // 打印实例化对象xiaohong

this 指向的是实例化对象 xiaohong。

class 关键字

自 ES6 起,引入了 class 关键字用于创建类。

class Person {
constructor(name, age) {
this.name = name
this.age = age
}
run() {
return 'run...'
}
eat() {
return 'eat...'
}
}

let xiaohong = new Person('小红', 11)
let xiaoming = new Person('小明', 10)
console.dir(Person) // 输出构造函数
console.dir(xiaohong) // 输出实例化对象

在类中定义的方法,会自动将方法定义在它的原型中。这与上一小节利用构造函数定义的类没有任何区别。因为 class 关键字并未改 JavaScript 类基于原型的本质,它只是定义类的“语法糖”。

static 关键字

静态属性和静态方法属于类,而不属于任何单个对象。在 JavaScript 中,静态属性和静态方法是在构造函数而非实例对象上定义的,所以静态属性和静态方法也称之为类属性和类方法。

静态方法和属性

普通方法和普通属性属于所有实例对象,所以只能以实例对象来调用。静态方法和静态属性是所有对象共享的,所以只能由构造函数名(或类名)来调用。

function Person(name, age) {
this.name = name
this.age = age
}

// 定义静态方法run
Person.run = function run() {
console.log('run...')
}

// 定义静态属性type
Person.type = '人类'

let p = new Person('小明', 12)
Person.run() // 调用静态方法run
Person.type // 调用静态属性type => '人类'

因为静态属性和静态方法是定义在构造函数之上的,所以 Person 的原型 constructor 属性下有静态方法 run 和静态属性 type,而不是 Person 实例对象上的,该实例对象只有 age 和 name 这两个非共享(独立的)的属性。

下面将利用 class 关键字定义类。在 class 关键字定义的类中,只需要在普通方法和属性前加上 static 关键字。

class Person {
static type = '人类'

constructor(name, age) {
this.name = name
this.age = age
}

run() {
console.log('normal run...')
}

static run() {
console.log('static run...')
}
}

let p = new Person('小明', 12)
Person.run() // 调用静态方法run
Person.type // 调用静态属性type => '人类'

私有属性

类属性在默认情况下是公共的,可以被外部类检测或修改。在ES2020 实验草案中,增加了定义私有类字段的能力,写法是使用一个 # 作为前缀。

class Person {
#name

get name() {
return this.#name
}

set name(name) {
this.#name = name
}
}

私有属性在 Java 中被体现得尤为突出。通常类的属性会被加上 private 使其私有化。这样做的目的是防止外部访问时,意外地修改属性。并且会有一对对应的 getter 和 setter 来操作私有属性。

子类

在面向对象编程中,类 B 可以继承类 A,类 A 是父类,类 B 是子类。类 B 继承类 A 的方法,类 B 也可以定义自己的方法。

在 ES6 及以后,要继承父类,可以简单地在类声明中加上一个extends子句,甚至对内置的类也可以这样。

class EZArray extends Array {
get first() { return this[0] }
get last() { return this[this.length - 1] }
}

let a = new EZArray()
a instanceof EZArray // true,a是子类的实例
a instanceof Array // true,a是父类的实例
a.push(1, 2, 3, 4) // 子类继承的父类的方法
a.first() // 子类定义的方法
a[1] // 数组的属性访问表达式仍然有效
Array.isArray(a) // true,子类的实例是数组
EZArray.isArray(a) // true,子类继承了静态方法

案例来源于《JavaScript权威指南》- 第9章 - 216页。

一个类使用 extends 继承父类,那么子类的构造函数必须使用 super() 调用父类构造函数。如果在子类中没有定义构造函数,解释器会自动创建一个。

class NormalMap extends Map {} // 未定义构造函数,解释器自动创建
let normalMap = new NormalMap()

class UnnormalMap extends Map { // 定义构造函数,但未调用super()
constructor(keyType, valueType) {
this.keyType = keyType
this.valueType = valueType
}
}
let unnormalMap = new UnnormalMap()

UnnormalMap 子类在构造函数中未调用 super() ,因此抛出 ReferenceError 异常。在调用了 super() 之后,一定要确保它在 this 关键字之前,否则也会抛出异常。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: