JS Class 类

发布于 2024-12-31 01:23:04 字数 8061 浏览 12 评论 0

类的数据类型就是函数,类本身就指向构造函数。

ES5 生成实例对象的传统方法是通过构造函数

function Point(x, y) {
  this.x = x
  this.y = y
}
Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')'
}
var p = new Point(1, 2)

ES6

class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }         //没有逗号
  toString() {
    return '(' + this.x + ', ' + this.y + ')'
  }
}
var p = new Point(1, 2)

所以

class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
//类的数据类型就是函数,类本身就指向构造函数。

var p = new Point(1, 2)
p.constructor === Point.prototype.constructor // true
//实例的 constructor 属性,指向构造函数 Point

为类的原型对象添加属性

class Point {
  constructor(){
    // ...
  }
  toString(){}
  toValue(){}
}
// 或者
Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
})

类的内部所有定义的方法,都是不可枚举的(non-enumerable)

class Point {
  constructor(x, y) {
    // ...
  }
  toString() {
    // ...
  }
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

ES5 不一样
var Point = function (x, y) {
  // ...
}
Point.prototype.toString = function() {
  // ...
}
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

constructor 方法

没有显式定义,一个空的 constructor 方法会被默认添加

class Point {}
// 等同于
class Point {
  constructor() {}
}

通过 new 命令生成对象实例时,自动调用该方法,返回实例对象(即 this),当然也可以返回另一个对象

class Foo {
  constructor() {
    return Object.create(null)
  }
}
new Foo() instanceof Foo // false

类必须使用 new 调用

取值函数(getter)和存值函数(setter)

与 ES5 一样,在“类”的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter'
  }
  set prop(value) {
    console.log('setter: '+value)
  }
}
let inst = new MyClass()
inst.prop = 123
// setter: 123
inst.prop
// 'getter'

存值函数和取值函数是设置在属性的 Descriptor 对象上的,与 ES5 完全一致。

class CustomHTMLElement {
  constructor(element) {
    this.element = element
  }
  get html() {
    return this.element.innerHTML
  }
  set html(value) {
    this.element.innerHTML = value
  }
}
var descriptor = Object.getOwnPropertyDescriptor(
  CustomHTMLElement.prototype, "html"
)
"get" in descriptor  // true
"set" in descriptor  // true

Class 表达式

与函数一样,类也可以使用表达式的形式定义。

const MyClass = class Me {
  getClassName() {
    return Me.name
  }
}
// Me 只在 Class 的内部可用,在 Class 外部,这个类只能用 MyClass 引用

// 也可以简写
const MyClass = class { /* ... */ }

立即执行的 Class

let person = new class {
  constructor (name) {
    this.name = name
  }

  sayName () {
    console.log(this.name)
  }
}('张三')
persion.sayName() // 张三

注意

  1. 严格模式 类和模块的内部,默认就是严格模式
  2. 不存在变量提升
new Foo() // ReferenceError
class Foo {}

this 的指向 类的方法内部如果含有 this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

class Logger {
printName(name = 'there') {
 this.print(`Hello ${name}`)
}
print(text) {
 console.log(text)
}
}
const logger = new Logger()
const { printName } = logger
printName() // TypeError: Cannot read property 'print' of undefined(严格模式不能指向 window)

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
static classMethod() {
 return 'hello'
}
}
Foo.classMethod() // 'hello'
var foo = new Foo()
foo.classMethod()
// TypeError: foo.classMethod is not a function

如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例

class Foo {
static bar() {
 this.baz()    //相当于 Foo.baz()
}
static baz() {
 console.log('hello')
}
baz() {
 console.log('world')
}
}
Foo.bar() // hello

父类的静态方法,可以被子类继承

class Foo {
static classMethod() {
 return 'hello'
}
}
class Bar extends Foo {}
Bar.classMethod() // 'hello'

静态方法也是可以从 super 对象上调用的

class Foo {
static classMethod() {
 return 'hello'
}
}
class Bar extends Foo {
static classMethod() {
 return super.classMethod() + ', too'
}
}
Bar.classMethod() // "hello, too"

实例属性的新写法

实例属性除了定义在 constructor() 方法里面的 this 上面,也可以定义在类的最顶层

class IncreasingCounter {
constructor() {
 this._count = 0
}
get value() {
 console.log('Getting the current value!')
 return this._count
}
increment() {
 this._count++
}
}
//等同于
class IncreasingCounter {
_count = 0
get value() {
 console.log('Getting the current value!')
 return this._count
}
increment() {
 this._count++
}
}

静态属性

静态属性指的是 Class 本身的属性,即 Class.propName,而不是定义在实例对象(this)上的属性

// 老写法
class Foo { // ... } Foo.prop = 1 // 新写法
class Foo { static prop = 1 }

私有属性私有方法

私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。

class Foo {
  #a
  #b
  constructor(a, b) {
    this.#a = a
    this.#b = b
  }
  #sum() {
    return #a + #b
  }
  printSum() {
    console.log(this.#sum())
  }
}

new.target 属性

该属性一般用在构造函数之中,返回 new 命令作用于的那个构造函数。
如果构造函数不是通过 new 命令或 Reflect.construct() 调用的,new.target 会返回 undefined

function Person(name) {
  if (new.target === Person) {
    this.name = name
  } else {
    throw new Error('必须使用 new 命令生成实例')
  }
}
var person = new Person('张三') // 正确
var notAPerson = Person.call(person, '张三')  // 报错

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle)
    this.length = length
    this.width = width
  }
}
var obj = new Rectangle(3, 4) // 输出 true

子类继承父类时,new.target 会返回子类

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle)
    // ...
  }
}

class Square extends Rectangle {
  constructor(length) {
    super(length, width)
  }
}
var obj = new Square(3) // 输出 false
// 利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。
class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化')
    }
  }
}
class Rectangle extends Shape {
  constructor(length, width) {
    super()
    // ...
  }
}
var x = new Shape()  // 报错
var y = new Rectangle(3, 4)  // 正确

综合

class Rectangle {
  // constructor
  constructor(height, width) {
    // 实例属性(必须定义在类的方法里)
    this.height = height
    this.width = width
  }
  // 原型链上的 Getter
  get area () {
    return this.calcArea()
  }
  // 原型链上的方法
  calcArea () {
    return this.height * this.width
  }
  // 静态方法(不需要实例化该类,不能通过实例调用静态方法)
  static calcGirth (height, width) {
       return height * 2 + width * 2
  }
  static getDoubleStaticWidth () {
      return this.staticWidth * 2    // this 表示类构造函数
  }
  getDoubelPrototypeWidth () {
      return this.prototypeWidth * 2    // this 表示类实例,此时 prototypeWidth 从原型链继承过来
  }
}
// 静态属性(必须定义在类定义的外面)
Rectangle.staticWidth = 20
// 原型的数据属性(必须定义在类定义的外面)
Rectangle.prototype.prototypeWidth = 25
const square = new Rectangle(10, 10);

console.log(square.area)    // 100
console.log(square.calcArea())    // 100

console.log(square.calcGirth(10, 10))    // square.calcGirth is not a function
console.log(Rectangle.calcGirth(10, 10))    // 40

console.log(Rectangle.getDoubleStaticWidth()) // 40
console.log(square.getDoubelPrototypeWidth()) // 50

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

白芷

暂无简介

文章
评论
27 人气
更多

推荐作者

闻呓

文章 0 评论 0

深府石板幽径

文章 0 评论 0

mabiao

文章 0 评论 0

枕花眠

文章 0 评论 0

qq_CrTt6n

文章 0 评论 0

红颜悴

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文