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)

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM