类
基础知识
为了和其他语言继承形态一致,JS提供了class 关键词用于模拟传统的class ,但底层实现机制依然是原型继承。
class 只是语法糖为了让类的声明与继承更加简洁清晰。
声明定义
可以使用类声明和赋值表达式定义类,推荐使用类声明来定义类
// 声明式
class User {}
// 赋值
const User = class {}
类方法间不需要逗号
class User {
get() {}
set() {}
}
构造函数
使用 constructor 构造函数传递参数,下例中show为构造函数方法,getName为原型方法
constructor 会在 new 时自动执行
class User {
constructor(name) {
this.name = name
this.show = function() {}
}
getName() {
return this.name
}
}
const m = new User('mkimq')
console.log(m)
构造函数用于传递对象的初始参数,但不是必须定义的,如果不设置系统会设置如下类型
- 子构造器中调用完super 后才可以使用 this
- 至于 super 的概念会在后面讲到
constructor(...args) {
super(...args)
}
原理分析
- 类其实是函数
- 在类中定义的方法也保存在函数原型中
class User {
constructor(name) {
this.name = name
}
show() {
console.log(this.name)
}
}
function User(name) {
this.name = name
}
User.prototype.show = function() {
console.log(this.name)
}
属性定义
在class中定义的属性为每个new出的对象独立创建,下面定义了site与name两个对象属性
class User {
site = 'mkimq'
constructor(name) {
this.name = name
}
show() {
console.log(this.site + ':' + this.name)
}
}
const m = new User('mankeung')
m.show()
函数差异
class 是使用函数声明类的语法糖,但也有些区别
class 中定义的方法不能枚举
class User {
constructor(name) {
this.name = name
}
show() {
console.log(this.name)
}
}
const m = new User('mkimq')
// 不会枚举出show属性
for (const key in m) {
console.log(key)
}
function Fn(name) {
this.name = name
}
Fn.prototype.show = function() {
console.log(this.name)
}
const f = new Fn('f')
for (const key in f) {
console.log(key)
}
严格模式
class 默认使用strict 严格模式执行
class User {
constructor(name) {
this.name = name
}
show() {
function test() {
//严格模式下输出 undefined
console.log(this)
}
test()
}
}
const m = new User('mkimq')
m.show()
function Fn(name) {
this.name = name
}
Fn.prototype.show = function() {
function test() {
// 非严格模式输出 Window
console.log(this)
}
test()
}
const f = new Fn('f')
f.show()
静态访问
静态属性
静态属性即为类设置属性,而不是为生成的对象设置,下面是原理实现
function User() {}
User.site = 'mkimq'
console.log(User)
const m = new User()
console.log(m.site)
console.log(User.site)
在class中为属性添加static关键字即声明为静态属性
- 可以把为所有对象使用的值定义为静态属性
class User {
static site = 'mkimq'
show() {
return User.site
}
}
const m = new User()
console.log(m.show())
console.log(User.site)
静态方法
指通过类访问不能使用对象访问的方法,比如系统的Math.round()就是静态方法
- 一般来讲方法不需要对象属性参与计算就可以定义为静态方法
下面是静态方法实现原理
function User() {
this.show = function() {
console.log('this is a object function')
}
}
User.show = function() {
console.log('this is static method')
}
const m = new User()
m.show()
User.show()
在class内声明的方法前使用static定义的方法即是静态方法
class User {
show() {
console.log('this is a object function')
}
static show() {
console.log('this is static method')
}
}
const m = new User()
m.show()
User.show()
访问器
使用访问器可以对对象的属性进行访问控制,下面是使用访问器对私有属性进行管理。
语法介绍
使用访问器可以管控属性,有效的防止属性随意修改
- 访问器就是在函数前加上 get/set修饰,操作属性时不需要加函数的扩号,直接用函数名
class User {
constructor(name) {
this.data = { name }
}
get name() {
return this.data.name
}
set name(value) {
if (value.trim() == '') throw new Error('invalid params')
this.data.name = value
}
}
const m = new User('mkimq')
m.name = 'MKIMQ'
console.log(m.name)
访问控制
设置对象的私有属性有多种方式
public
public 指不受保护的属性,在类的内部与外部都可以访问到
class User {
site = 'mkimq'
constructor(name) {
this.name = name
}
}
const m = new User('mankeung')
console.log(m.site, m.name)
protected
protected是受保护的属性修释,不允许外部直接操作,但可以继承后在类内部访问,有以下几种方式定义
class User {
_site = 'mkimq'
constructor(name) {
this.name = name
}
}
// 继承时可以使用
class Admin extends User {
show() {
return this._site
}
}
使用Symbol定义私有访问属性,即在外部通过查看对象结构无法获取的属性
const protecteds = Symbol()
class User {
constructor() {
this[protecteds] = {}
this[protecteds].site = 'mkimq'
}
}
WeakMap 是一组键/值对的集,下面利用WeakMap类型特性定义私有属性
const protecteds = new WeakMap()
class User {
constructor() {
protecteds.set(this, 'mkimq')
}
}
private
private 指私有属性,只在当前类可以访问到,并且不允许继承使用
- 为属性或方法名前加 # 为声明为私有属性
- 私有属性只能在声明的类中使用
class User {
#site = 'mkimq'
#check() {}
}
属性保护
保护属性并使用访问器控制
class User {
constructor(name) {
this.data = { name }
}
get name() {
return this.data.name
}
set name(value) {
if (value.trim() == '') throw new Error('invalid params')
this.data.name = name
}
}
详解继承
属性继承
属性继承的原型如下
function User(name) {
this.name = name
}
function Admin(name) {
User.call(this, name)
}
const m = new Admin('mkimq')
console.log(m)
类构造函数
class User {
constructor(name) {
this.name = name
}
}
class Admin extends User {
constructor(name) {
super(name)
}
}
const m = new Admin('mkimq')
console.log(m)
方法继承
原生的继承主要是操作原型链,实现起来比较麻烦,使用 class 就要简单的多了。
- 继承时必须在子类构造函数中调用 super() 执行父类构造函数
- super.show() 执行父类方法
class User {
constructor(name) {
this.name = name
}
show() {
return this.name
}
}
class Admin extends User {
constructor(name) {
super(name)
}
run() {
return super.show()
}
}
const m = new Admin('mkimq')
console.log(m.run())
也可以使用 extends 继承表达式返回的类
super
- 表示从当前原型中执行方法,
- super 一直指向当前对象
super 只能在类或对象的方法中使用,而不能在函数中使用,下面将产生错误
constructor
- super 指调父类引用,在构造函数constructor 中必须先调用super()
- super() 指调用父类的构造函数
- 必须在 constructor 函数里的this 调用前执行 super()
父类方法
使用super可以执行父类方法
super.show()
方法覆盖
子类存在父类同名方法时使用子类方法
静态继承
静态的属性和方法也是可以被继承使用的
对象检测
- instanceof
console.log(m instanceof User)
- isPrototypeOf
User.prototype.isPrototypeOf(m)
继承内置类
function Arr(...args) {
args.forEach(item => this.push(item))
this.first = function() {
return this[0]
}
this.max = function() {
return this.sort((a, b) => b -a)[0]
}
}
Arr.prototype = Object.create(Array.prototype)
const arr = new Arr(1, 2, 3)
arr.forEach(item => {
console.log(item)
})
console.log(arr.first())
class Arr extends Array {
constructor(...args) {
super(...args)
}
first() {
return this[0]
}
add(value) {
return this.push(value)
}
remove(value) {
const pos = this.findIndex(curValue => {
return curValue == value
})
this.splice(pos, 1)
}
}
const m = new Arr(1, 2, 3)
console.log(m.length)
console.log(m.first())
m.add('mkimq')
console.log(m.join('-'))
mixin
JS不能实现多继承,如果要使用多个类的方法时可以使用mixin混合模式来完成。
- mixin 类是一个包含许多供其它类使用的方法的类
- mixin 类不用来继承做为其它类的父类
const Tool = {
max(key) {
return this.data.sort((a, b) => b[key] - a[key])[0]
}
}
class Lesson {
constructor(lession) {
this.lession = lession
}
get data() {
return this.lession
}
}
Object.assign(Lesson.prototype, Tool)
const data = [
{
name: 'js',
price: 100
},
{
name: 'ts',
price: 90
},
{
name: 'node',
price: 88
},
]
const m = new Lesson(data)
console.log(m.max('price'))