JS Class 的继承

发布于 2025-02-25 07:37:13 字数 5069 浏览 3 评论 0

可以通过 extends 关键字实现继承

class Point {}
class ColorPoint extends Point {}
// 由于没有部署任何代码,所以这两个类完全一样,等于复制了一个 Point 类。

super() 函数

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y) // 调用父类的 constructor(x, y)
    this.color = color
  }

  toString() {
    return this.color + ' ' + super.toString() // 调用父类的 toString()
  }
}
// 注意一个是构造函数,一个是父类原型

子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。

class Point { /* ... */ }
class ColorPoint extends Point {
  constructor() {}
}
let cp = new ColorPoint() // ReferenceError

所以

class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
  constructor(...args) {        //默认被添加
    super(...args)
  }
}

在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字

class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color // ReferenceError
    super(x, y)
    this.color = color // 正确
  }
}
class A {
  constructor() {
    console.log(new.target.name)
  }
}
class B extends A {
  constructor() {
    super()        //相当于 A.prototype.constructor.call(this)
  }
}
new A() // A
new B() // B

只能用在子类的构造函数之中,用在其他地方就会报错

class A {}

class B extends A {
  m() {
    super() // 报错
  }
}

super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
由于 super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。

class A {
  constructor() {
    this.p = 2     // 父类实例的 p
  }
}
class B extends A {
  get m() {
    return super.p     // 父类原型对象上面的 p
  }
}
let b = new B()
b.m // undefined

由于 this 指向子类实例,所以如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性。

class A {
  constructor() {
    this.x = 1
  }
}
class B extends A {
  constructor() {
    super()
    this.x = 2
    super.x = 3
    console.log(super.x) // undefined
    console.log(this.x) // 3
  }
}
let b = new B()

如果 super 作为对象,用在静态方法之中,这时 super 将指向父类,而不是父类的原型对象。

class Parent {
  static myMethod(msg) {
    console.log('static', msg)
  }
  myMethod(msg) {
    console.log('instance', msg)
  }
}
class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg)
  }
  myMethod(msg) {
    super.myMethod(msg)
  }
}
Child.myMethod(1) // static 1
var child = new Child()
child.myMethod(2) // instance 2
// super 在静态方法之中指向父类,在普通方法之中指向父类的原型对象

在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类

class A {
  constructor() {
    this.x = 1
  }
  static print() {
    console.log(this.x)
  }
}
class B extends A {
  constructor() {
    super()
    this.x = 2
  }
  static m() {
    super.print() // 此时 this 指 B
  }
}
B.x = 3
B.m() // 3

类的 prototype 属性和 proto 属性

class A {}
class B extends A {}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
class A {}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true
// 这种情况下,A 作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承 Function.prototype。但是,A 调用后返回一个空对象(即 Object 实例),所以 A.prototype.__proto__指向构造函数(Object)的 prototype 属性。

原生构造函数的继承

原生构造函数是指语言内置的构造函数,通常用来生成数据结构。
ECMAScript 的原生构造函数大致有下面这些:

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object() 以前这些原生构造函数是无法继承的,比如,不能自己定义一个 Array 的子类。
function MyArray() {
Array.apply(this, arguments)
}
MyArray.prototype = Object.create(Array.prototype, {
constructor: {
 value: MyArray,
 writable: true,
 configurable: true,
 enumerable: true
}
})

上面代码定义了一个继承 Array 的 MyArray 类。但是,这个类的行为与 Array 完全不一致。

var colors = new MyArray() colors[0] = "red" colors.length // 0
colors.length = 0 colors[0] // "red"

原因:原生构造函数会忽略 apply 方法传入的 this,也就是说,原生构造函数的 this 无法绑定,导致拿不到内部属性。

而现在

class MyArray extends Array {
  constructor(...args) {
    super(...args)
  }
}

var arr = new MyArray()
arr[0] = 12
arr.length // 1

arr.length = 0
arr[0] // undefine

上面代码定义了一个 MyArray 类,继承了 Array 构造函数,因此就可以从 MyArray 生成数组的实例。这意味着,ES6 可以自定义原生数据结构(比如 Array、String 等)的子类,这是 ES5 无法做到的

有一个行为差异

class NewObj extends Object{
  constructor(){
    super(...arguments)
  }
}
var o = new NewObj({attr: true})
o.attr === true  // false
// 一旦发现 Object 方法不是通过 new Object() 这种形式调用,ES6 规定 Object 构造函数会忽略参数。

var o = new Object({attr: true})
o.attr === true  // true

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

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

上一篇:

下一篇:

发布评论

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

关于作者

七七

暂无简介

文章
评论
27 人气
更多

推荐作者

闻呓

文章 0 评论 0

深府石板幽径

文章 0 评论 0

mabiao

文章 0 评论 0

枕花眠

文章 0 评论 0

qq_CrTt6n

文章 0 评论 0

红颜悴

文章 0 评论 0

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