js中的class
概述
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。class 的本质是 function。它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
重点在于构造函数,使用的是构造函数来模拟类。
类的声明
声明一个类,需要使用class关键字
// 正常声明一个类
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// 匿名一个类
let Rectangle1 = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// 具名一个类
let Rectangle2 = class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
注意,和函数声明的最大的区别在于,类声明不会出现声明提前。
当然,类也不允许重复声明
class Example1{}
class Example1{}
// Uncaught SyntaxError: Identifier 'Example' has already been declared
let Example2 = class{}
class Example2{}
// Uncaught SyntaxError: Identifier 'Example2' has already been declared
类的主体
prototype
ES6 中,prototype 仍旧存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。 我们可以使用它为已存在的类覆盖方法 / 初始化时添加方法
// 声明一个类
class Example3 {
say() {
console.log('hi Class !')
}
}
// 类的方法被覆盖了 - Chrome80版本,覆盖无效
Example3.prototype = {
say() {
console.log('hello Class !')
}
}
// 为类添加方法
Object.asign(Example.prototype, {
listen() {
console.log('hello !')
}
})
// 删除类上的方法
Reflect.deleteProperty(Example3.prototype, 'say')
static
静态属性,class本身的属性,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。
新的提案,Chrome已经支持Class静态属性了
// 声明一个带有静态属性的类
class Example4 {
// 新的提案
static a = 1
}
// 类本身可以直接调用
Example4.a // 1
// 也可以直接赋值
Example4.b = 2
Example4.b // 2
通过new 关键字生成的对象,无法调用该方法
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
static distance(a, b) {
const dx = a.x - b.x
const dy = a.y - b.y
return Math.hypot(dx, dy) // 毕达哥拉斯定理
}
}
const p1 = new Point(5, 5)
const p2 = new Point(10, 10)
console.log(Point.distance(p1, p2)) // 7.0710678118654755
上方的p1, p2对象中找不到静态方法distance
console.log(p1) // Point {x: 5, y: 5}
console.log(p2) // Point {x: 10, y: 10}
console.log(p1.distance({x: 5, y: 5}, {x: 10, y: 10})) // Uncaught TypeError: p1.distance is not a function
public
公共属性,class上的公共属性可以被所有实例化对象访问,该属性被挂载在class原型对象上
class Example8 {}
Example8.prototype.a = 2
const example8 = new Example8
example8.a // 2
instance
实例属性,定义在实例对象(this)上的属性
class Example9 {
a = 2
constructor() {
console.log(this.a)
}
}
const example9 = new Example9
example9 // Example9 {a: 2}
name
该属性能获取或者返回class的类名
let Example5 = class Exam {
a = 1
constructor() {
console.log(this.a)
}
}
console.log(Example5.name) // Exam
let Example6 = class {
a = 2
constructor() {
console.log(this.a)
}
}
console.log(Example6.name) // Example6
constructor
该方法是类的默认方法,创建类的实例化对象时被调用
class Rectangle {
// 构造函数
constructor(height, width) {
this.height = height
this.width = width
console.log('我是constructor')
}
// get 方法会在对象创建后立即调用并返回调用结果
get area() {
return this.calcArea()
}
// 定义calcArea函数
calcArea() {
return this.height * this.width
}
}
使用被声明的类
const square = new Rectangle(10, 10) // '我是constructor'
console.log(square.area) // 100
constructor没有返回值时,默认返回实例化对象this,其原型是当前的Class
console.log(square instanceof Rectangle) // true
constructor指定返回值时,返回被指定的对象,其原型是被指向对象的原型
class Example7 {
constructor() {
// 指定返回值
return new Rectangle()
}
}
console.log(new Example7() instanceof Example7) // false
console.log(new Example7() instanceof Rectangle) // true
实例化对象的_proto_(构造器原型)等于该对象构造函数的prototype(原型) - 中文描述像是废话,下面是代码
square.__proto__ === square.constructor.prototype // true
square.__proto__ === Rectangle.prototype // true
square.__proto__.constructor === Rectangle // true
Rectangle.prototype.constructor === Rectangle // true
静态方法、原型方法与实例方法
// 静态
class StaticFunc {
static sum(a, b) {
console.log(a + b)
}
}
// 调用方式
StaticFunc.sum(1, 2) // 3
// 原型
class PrototypeFunc {
sum(a, b) {
console.log(a + b)
}
}
const protoFunc = new PrototypeFunc()
protoFunc.sum(1, 2) // 3
// 实例
class InstanceFunc {
constructor() {
this.sum = (a, b) => {
console.log(a + b)
}
}
}
// 实例化时被调用,为实例化对象增加方法
const instanceFunc = new InstanceFunc()
instanceFunc.sum(1, 2) // 3
super
使用super调用超类
class Cat {
constructor(name) {
this.name = name
}
speck() {
console.log(this.name)
}
}
class Lion extends Cat {
listen() {
super.speck()
}
}
let l = new Lion('Mickey')
l.speck() // 'Mickey'
l.listen() // 'Mickey'
extends
使用extends关键字创建子类
class Animal {
constructor(name) {
this.name = name
}
// 类中创建的函数,可以省略function关键字
speck() {
console.log(this.name)
}
}
// 创建Animal的子类 - 子类拥有父类的全部方法,还拥有私有方法
class Dog extends Animal {
// 父类不存在的私有方法
listen() {
console.log(this.name)
}
}
let d = new Dog('Turkey')
d.speck() // 'Turkey'
d.listen() // 'Turkey'
封装与继承
getter / setter
class Example{
constructor(a, b) {
this.a = a // 实例化时调用 set 方法
this.b = b
}
get a(){
console.log('getter')
return this.a
}
set a(a){
console.log('setter')
this.a = a // 自身递归调用
}
}
let exam = new Example(1, 2) // 不断输出 setter ,最终导致 RangeError
class Example1{
constructor(a, b) {
this.a = a
this.b = b
}
get a(){
console.log('getter')
return this._a
}
set a(a){
console.log('setter')
this._a = a
}
}
let exam1 = new Example1(1, 2) // 只输出 setter , 不会调用 getter 方法
console.log(exam._a) // 1, 可以直接访问
getter 不可单独出现
class Example {
constructor(a) {
this.a = a
}
get a() {
return this.a
}
}
let exam = new Example(1) // Uncaught TypeError: Cannot set property a of #<Example> which has only a getter
getter 与 setter 必须同级出现
class Father {
constructor() {}
get a() {
return this._a
}
}
class Child extends Father {
constructor() {
super()
}
set a(a) {
this._a = a
}
}
let test = new Child()
test.a = 2
console.log(test.a) // undefined
class Father1 {
constructor() {}
// 或者都放在子类中
get a() {
return this._a
}
set a(a) {
this._a = a
}
}
class Child1 extends Father1 {
constructor() {
super()
}
}
let test1 = new Child1()
test1.a = 2
console.log(test1.a) // 2
注意
类不可以继承常规的对象
var Father = {
// ...
}
class Child extends Father {
// ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null
// 解决方案
Object.setPrototypeOf(Child.prototype, Father)