在ES6
中之前如果我们想实现类
只能通过原型链和构造函数
的形式,不仅难以理解步骤也十分繁琐
在ES6
中推出了class
关键字,它可以在js
中定一个类,通过new
来实例化这个类的对象
需要注意的是class
关键字只是一个语法糖
,它的本质还是通过原型链和构造函数
来实现的
class
中的代码默认都是运行在strict
模式下的
以下是一个简单的class
关键字用法
class Person {
name = "zhangsan";
}
let people = new Person();
let Person2 = class {
name = "lisi";
};
let people2 = new Person2();
console.log(people, people2);
可以看到类有两个定义方法,一个是类声明
,即class 类名
,还有一个是类表达式
,类表达式可以命名也可以不命名,即 var/let/const 局部名称 = class [类名] {}
无论是类声明
还是类表达式
都不会提升
,想要使用类就必须在类定义
的后面
这种定义在class
中的属性被称为公有字段
,如果在字段前加上#
就成了私有字段
如果我们希望在构造对象的时候传入一些参数
就需要修改这个类的构造函数constructor
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
}
let people = new Person("zhangsan", 18, "wuhan")
console.log(people)
构造函数
的名称确定为constructor
,如果一个类
中含有多个constructor
则会抛出一个错误
能被多个实例共享
的方法
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
eating() {
console.log(this.name + "eating...")
}
running() {
console.log(this.name + " running...")
}
}
let people = new Person("zhangsan", 18, "wuhan")
console.log(people)
people.running()
people.running()
我们可以针对对应的属性
设置get
和set
来拦截
原本的修改和读取操作
class Person {
constructor(name, age, address) {
this._name = name
this.age = age
this.address = address
}
get name() {
console.log("执行了读取操作")
return this._name
}
set name(newName) {
console.log("执行了设置操作")
this._name = newName
}
}
let people = new Person("zhangsan", 18, "wuhan")
console.log(people.name)
people.name = "lisi"
注意,如果get
或set
与属性名重复
的话就会发生死循环
静态方法
则是指直接由类
来调用的方法,不需要实例,通过static
来定义静态方法
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
static birth(name, age, address) {
return new Person(name, age, address)
}
}
let people = Person.birth("wangwu", 0, "wuhan")
console.log(people)
在ES6
之前我们想要实现继承
是十分麻烦且繁琐的,如何在ES6
之前实现继承
可以看我这篇文章
js原型与原型链
而在ES6
中有了新的关键字extends
可以更方便的实现继承
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
}
class Man extends Person {
}
super
关键字可以访问父类的方法与属性
需要注意的是在constructor
中调用super
前不能使用this
为什么在调用super
前不能访问this
可以看我这篇文章
未动笔,未来可寄
super.prop
可以访问父类的静态属性与方法
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
eating() {
console.log(this.name + "eating...")
}
}
class Man extends Person {
constructor(...args) {
super(...args)
}
eating() {
super.eating()
}
}
let people = new Man("zhangsan", 18, "wuhan")
console.log(people)
people.eating()
因为js
的继承
只能继承一个类
,因为函数
的显式原型
不可能有多个
如果我们想要让子类
有尽可能多的复用其他类就可以使用混入
混入
的原理十分简单,假如我们有ABC三个类,我们希望C能继承AB两个类,思路就是我们可以先让B继承A,再让C继承B
我们可以将这个过程封装成一个函数
再extends
class Person {
constructor(name, age, address) {
this.name = name
this.age = age
this.address = address
}
eating() {
console.log(this.name + "eating...")
}
}
function minix(BaseClass) {
return class extends BaseClass {
sleeping() {
console.log(this.name + " sleeping...")
}
}
}
class Man extends minix(Person) {
constructor(...args) {
super(...args)
}
eating() {
super.eating()
}
}
let people = new Man("zhangsan", 18, "wuhan")
console.log(people)
people.eating()
people.sleeping()
我觉得js
是有多态
,多态的概念很简单:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
就比如a+b,如果传入的是1和2,返回的自然是3,如果传入的是"1"和"2",返回的就是"12"
这么一看js
也确实符合
多态
最常用的实现方式就是重载
与重写
重写
在原型链
上已经实现了不必再说,关键就在于重载
,重载
指同名但参数不同的方法
,但js
中方法本身就可以传任意数量任意类型
的参数,我们可以通过if
来判断不同类型的参数执行什么操作,但这个算不算重载
也很难说
我认为js
作为一门弱类型
的语言天然就应该是具有多态
这个特性的