(七) class类
1. ES5中的类
在es5中, 我们约定俗成的将首字母大写的函数称为构造函数
, 我们可以通过构造函数来进行实例化, 在其他高级语言中, 我们或多或少都看过这样的说明: 类
是一类事物的抽象, 而对象
是类
的具体表现, 但是在es5中, 并没有类的概念, 而是将构造函数
认为是类
, 并通过原型绑定的方式来实现共有属性和方法的继承
示例
function Person(name) {
this.name = name
}
Person.prototype.sayName = function () {
console.log(this.name)
}
var p1 = new Person('猫13')
var p2 = new Person('猫14')
console.log(p1.sayName) // 猫13
console.log(p2.sayName) // 猫14
为了改变这种缺陷: 你说你是面向对象语言, 却连真正的类都没有实现, 这有点说不过去了吧, 所以, 在es6中, 引入了Class(类)
的概念
2. Class(类)
我们先来看一下Class
的基本使用语法:
示例 1
// class关键字
class Person {
// 构造方法 constructor
constructor(name) {
this.name = name
}
// 类方法
sayName() {
this.name = this.name || '猫14'
console.log(this.name)
}
}
let p = new Person('猫13')
p.sayName() // 猫13
其中constructor
方法是类的默认方法, 不管在声明类的时候有没有显式创建, 而当我们通过new
关键字创建实例对象时, 就会自动调用constructor
方法
class Person {
constructor() {
console.log(111111)
}
}
new Person() // 111
我们来看一个有意思的地方
// 代码为 示例1
console.log(typeof Person) // function
Person.prototype.sayName() // 猫14
我们使用class
定义的类竟然是一个function
类型, 而且Person
的原型上竟然也有sayName
方法 !
对比最开始我们再es5中使用构造函数创建 "类" 的方式, 我们发现: 其实es6中的class
只是对es5的一种封装, 说白了, class只是一个语法糖而已 , 虽然如此, class
的封装至少让我们不必再像以前那样繁琐, 并且还有了自身独特的新特性
2.1 class中属性和方法的本质
class Fn {
// name 相当于 es5的 this.name, 是成员属性
name = '猫13'
// say方法是定义在Fn的prototype上, 与es5一样
say() {
return 100
}
}
let f = new Fn()
console.log(f.name) // 猫13
console.log(Fn.prototype.name) // undefined
console.log(Fn.prototype.say()) // 100
2.2 class的新特性
- 类声明不会被提升, 与
let
声明类似 - 类内部有一个默认的方法
constructor
, 通过关键字new创建实例时会自动调用该方法, 返回实例对象, 即this
- 类内部定义的所有方法,都是不可枚举的
- 创建实例必须使用new关键字, 否则会报错
- 类中的所有代码都处于严格模式下
2.3 class表达式
与函数一样, class也可以使用表达式的形式来定义
let Person = class {
constructor(name) {
this.name = name
console.log(this.name)
}
}
new Person('猫13')
2.4 this问题
class的新特性之一: 内部默认使用严格模式
class Person {
callFn() {
this.fn()
}
fn() {
console.log('测试');
}
}
let p = new Person()
// p.callFn() // 测试
let f = p.callFn
f() // Cannot read property 'fn' of undefined
严格模式下, 直接调用函数(在全局或者其他函数内), this将保持进入执行环境的值,所以,如果没有指定环境,则默认undefined
function test() {
console.log(this) // undefined
(function(){
console.log(this) // undefined
})
}
test()
这也是上面调用f
为什么会报错的原因, 因为this的值是undefined
, 你再去调用函数, 就相当于
function fn() {}
undefined.fn()
@解决办法
bind
class Person {
// 通过bind提前绑定好this指向
constructor() {
this.callFn = this.callFn.bind(this)
}
callFn() {
this.fn()
}
fn() {
console.log('测试');
}
}
let p = new Person()
let f = p.callFn
f() // 测试
箭头函数
class Person {
// 箭头函数
callFn = () => {
this.fn()
}
fn() {
console.log('测试');
}
}
let p = new Person()
let f = p.callFn
f() // 测试
通过proxy
2.5 静态属性和方法
使用static
关键字声明, 定义的静态属性和方法, 是类属性和方法, 只能通过类名.属性名/方法名
的方式调用
class Person {
static name
static sayName() { }
}
Person.name
Person.sayName()
2.6 私有属性和方法 (es7)
只能在类的内部使用, 不对外暴露
class Math {
#count = 0
#sum() {
return ++this.#count
}
callSum() {
return this.#sum()
}
}
let math = new Math()
//
// console.log(math.#sum()) #sum' must be declared in an enclosing class
console.log(math.callSum()) // 1