当前位置: 首页 > 知识库问答 >
问题:

javascript - ES6 父子继承中一些执行顺序以及this指向的问题?

小牛23002
2023-12-28

有如下代码:

class Parent {  constructor() {    console.log('parent constructor', this.name)    this.init()    this.logNum = this.logNum.bind(this)  }  name = (() => {    console.log('Parent name init')    return 'Parent'  })()  num = -1  init() {    console.log('parent init', this.name)  }  logNum() {}}class Child extends Parent {  constructor() {    console.log('Child constructor')    super()    console.log('super exec finish', this.num)  }  name = 'Child'  num = (() => {    console.log('Child num init')    return 99  })()  init() {    console.log('Child init', this.name)    super.init()    this.num = 10  }  logNum() {    console.log(this.num)  }}const { logNum } = new Child()logNum()

打印结果:

Child constructorParent name init         parent constructor ParentChild init Parent        parent init Parent       Child num init           super exec finish 99     99       

1、在实例化Child时,Parent.constructor中的name为什么是'Parent'?
Child.constructor中调用super,内部this指向为Child,所以不应该是'Child'吗?

2、诸如x = 'a'的实例属性是什么时候完成初始化的?
Child.constructor中调用super时,可以看到打印了'Parent name init',说明实例属性是在构造器方法之前就初始化了吧,那为什么Child.num是在super调用结束后才初始化?

共有4个答案

司空瑾瑜
2023-12-28

代码可以转化为下列的形式

function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }class Parent {  constructor() {    _defineProperty(this, "name", (() => {      console.log('Parent name init');      return 'Parent';    })());    _defineProperty(this, "num", -1);    console.log('parent constructor', this.name);    this.init();    this.logNum = this.logNum.bind(this);  }  init() {    console.log('parent init', this.name);  }  logNum() {}}class Child extends Parent {  constructor() {    console.log('Child constructor');    super();    _defineProperty(this, "name", 'Child');    _defineProperty(this, "num", (() => {      console.log('Child num init');      return 99;    })());    console.log('super exec finish', this.num);  }  init() {    console.log('Child init', this.name);    super.init();    this.num = 10;  }  logNum() {    console.log(this.num);  }}const {  logNum} = new Child();
况唯
2023-12-28

1、创建子类实例对象,会在子类构造函数中 super 执行完之后才会把父类的实例属性和方法放到子类的实例对象上,所以在 super 中执行 this.name 时,子类的属性和方法还没有绑定到 this 上面去,此时会去父类查找同名的属性。

2、需要注意的是 super 代表的是父类的构造函数,但 super 内部的 this 代表子类的实例,而不是父类的实例。

这篇文章可能对你的问题能有更好的理解 Class的继承

羊舌源
2023-12-28

导致你产生这个疑问的原因是你对类成员初始化时机理解不准确,你可以把这段代码用babel做一个语法降级就能看出

  • 类成员初始化的时机是在 constructor 中,super() 执行完成之后进行的,也就是说,虽然 super() 中 this 指向是 Child 的实例,但其成员都还没有初始化,所以访问到的值还是 Parent 赋的默认值
    image.png
贺宝
2023-12-28

第一问

当实例化Child时,会首先执行父类Parent的构造函数,然后再执行子类Child的构造函数。
在Parent.constructor方法中打断点如下图所示:
image.png
从上图Scope栏中的Local作用域可知,虽然this值指向Child,但是其内部的name属性值是“Parent”

  • 原因是类字段初始化发生在构造函数执行之前。当 Parent 的构造函数被调用时,类字段的初始化,也就是表达式赋值

    name = (() => {  console.log('Parent name init')  return 'Parent'})()

    已经发生了。

  • 虽然this 引用的是 Child 的实例,但是因为表达式赋值是Parent 的构造函数被调用之前执行的,所以 this.name 实际上是在 Child 的实例上设置了一个名为 name 的属性,并赋值为 'Parent'。
  • 不妨在Parent中增加如下代码:

    parentName = "parentName"init() {  console.log('parent init', this.name)  //结果输出parent init Parent  console.log("test: ", this.parentName) //结果输出test: parentName}

    实际上还是先对类字段进行初始化,然后再调用构造函数实例化

第二问

当执行 super() 方法时,父类 Parent 的构造函数会执行并初始化父类的实例属性,然后才会继续执行子类 Child 的构造函数。在子类的构造函数中,才会对子类的实例属性比如child.num进行初始化

 类似资料:
  • 问题内容: 我有一个抽象基类,用作我的单元测试(TestNG 5.10)的基础。在该类中,我将初始化测试的整个环境,设置数据库映射等。此抽象类具有一个带有注释的方法,该方法可以进行初始化。 接下来,我用具有方法和方法的特定类扩展该类。这些方法对环境进行类特定的初始化(例如,将一些记录放入数据库中)。 如何执行带注释的方法的特定顺序?我需要先执行抽象基类中的那些,然后再执行扩展类中的那些。 例: 预

  • 为了说明这一点,我们创建一个动物园应用,其中创建鸟类。在经典继承中,我们定义一个基类,然后将其子类化以创建一个派生类。 类定义了Penguin类继承的方法walk,并且可以被Penguin对象的实例使用。 同样,Penguin类定义了不能用于Bird对象的方法swim。 继承从上到下从基类到它的子类。 对象初始化 对象创建的顺序从它的基类开始,然后向下移动到任何子类。 // JavaScript

  • 题目描述 如下图所示,利用chrome的devtools工具查看当前执行代码处的的Local Scope,发现此时this为undefined,但是为何this.visible在实际执行过程中没有报错? 题目来源及自己的思路 问题来源于vue文件中的一段函数代码,我的问题及思路如下 我知道箭头函数的this指向来源于所在上下文,此时应该是指向Vue实例 但是为何在devtool中Debug过程,此

  • 我在Java实践继承,面临以下问题: 父类的代码: 子类代码: 两者是分开的。java文件。当我尝试执行父类文件时,它可以工作,但由于某些原因,我甚至无法运行子类。这里有以下信息: 该文件于2月8日发布。java不是可执行的。请选择要运行的主类 我该如何解决这个问题?

  • 网址请求顺序是 souhu.com baidu.com tencent.com 这是什么原因呀?

  • 问题内容: 考虑下面的代码 基于我对类成员初始化和构造函数执行顺序的理解。我期望输出是 因为我相信类成员甚至在调用main方法之前就已初始化。但是当我运行程序时,我得到以下输出 我的困惑是,尽管Meal()Lunch()和PortableLunch()在Bread()Cheese()和Lettuce()之前运行,即使它们的构造函数被调用。 问题答案: 这些是实例字段 它们仅在创建实例时存在(执行)