简体   繁体   English

Javascript class 组成:在主 class 的属性中访问辅助 class 的属性

[英]Javascript class composition : access attribute of secondary class in attribute of main class

Let's take this exemple:让我们举个例子:

class A {
    attrA = 3;
}

class B {
    constructor() {
        this.a = new A;
    }
    attrB = this.a.attrA;
    methB() { console.log(this.a.attrA);}
}

const test = new B;

console.log(test.attrB);
test.methB();

I can access the class A attribute through method of class B, but I can't use it with attribute of class B, I have an "undefined" error.我可以通过 class B 的方法访问 class A 属性,但我不能将它与 class B 的属性一起使用,出现“未定义”错误。

The only way to do this is:唯一的方法是:

class A {
    attrA = 3;
}

class B {
    constructor() {
        this.a = new A;
        this.z = this.a.attrA
    }
    methB() { console.log(this.a.attrA);}
}

const test = new B;

console.log(test.z);
test.methB();

Why I need to put the attribute in the constructor and not the method?为什么我需要将属性放在构造函数中而不是方法中?

Thanks!谢谢!

You can think of class fields as being hoisted , meaning this:您可以将 class 字段视为已提升,这意味着:

class Example {
  constructor() {/*...*/}
  property = "value";
}

Is "actually" this: “实际上”是这样的:

class Example {
  property = "value";
  constructor() {/*...*/}
}

Or (similar to) this:或者(类似于)这个:

class Example {
  constructor() {
    this.property = "value";
    /*...*/
  }
}

Further, identifiers are resolved upon access .此外,标识符在访问时被解析 So by the time you execute test.methB() , test.a has been initialized, allowing the method to correctly resolve this.a.attrA .因此,当您执行test.methB()时, test.a已经初始化,允许该方法正确解析this.a.attrA It works the same as this code:它的工作原理与此代码相同:

 let variable = null; const logVariable = () => console.log(variable); // You may think this logs null, but... variable = "value"; logVariable(); //... it actually logs `variable`'s value at the time of calling.

As you have observed, mixing property initializations from within the constructor and using field initializers may be confusing.正如您所观察到的,混合使用构造函数中的属性初始化和使用字段初始化器可能会造成混淆。 Therefore, a more intuitive way to write your code would be:因此,编写代码的一种更直观的方法是:

 class A { attrA = 3; } class B { a = new A; attrB = this.a.attrA; constructor() {} methB() { console.log(this.a.attrA); } } const test = new B; console.log(test.attrB); // Works now. test;methB();

Personal recommendations:个人建议:

  • Declare all instance and class fields.声明所有实例和 class 字段。
  • Prefer field initializers.首选字段初始值设定项。
  • Only reassign/initialize fields in the constructor for non-default values.仅在构造函数中为非默认值重新分配/初始化字段。

You may want to read on for more details for a better technical understanding.您可能需要阅读更多详细信息以获得更好的技术理解。


Class syntax Class 语法

Classes are just uncallable constructor functions:类只是不可调用的构造函数:

 class Example {} console.log(typeof Example); // "function" Example(); // Error: cannot be invoked without `new`

This is a quirk of the ES6 class syntax .这是ES6 class 语法的怪癖。 Technically, class constructors are still "callable", but only internally (which happens when using new ).从技术上讲,class 构造函数仍然是“可调用的”,但仅限于内部(使用new时会发生)。 Otherwise constructors would serve no purpose.否则构造函数将毫无用处。

Another aspect of the class syntax is the ability to call super() in the constructor. class 语法的另一个方面是能够在构造函数中调用super() This only comes into play when a class inherits, but that comes with yet its own quirks:这只有在 class 继承时才会起作用,但这有它自己的怪癖:

You cannot use this before calling super :在调用super之前你不能使用this

 class A {}; class B extends A { constructor() { this; // Error: must call super before accessing this super(); } } new B();

Reason being, before calling super no object has been created, despite having used new and being in the constructor .原因是,在调用super之前没有创建 object,尽管使用了new并且在构造函数中

The actual object creation happens at the base class , which is in the most nested call to super .实际的 object 创建发生在 class 基础上,这是对super的最嵌套调用。 Only after calling super has an object been created, allowing the use of this in the respective constructor.只有在调用super之后才会创建 object ,允许在相应的构造函数中使用this

Class fields Class 个字段

With the addition of instance fields in ES13 , constructors became even more complicated: Initialization of those fields happens immediately after the object creation or the call to super , meaning before the statements that follow.随着 ES13 中实例字段的添加,构造函数变得更加复杂:这些字段的初始化在 object 创建或调用super之后立即发生,这意味着在后面的语句之前。

class A /*no heritage*/ {
  property = "a";

  constructor() {
    // Initializing instance fields

    // ... (your code)
  }
}

class B extends A {
  property = "b";

  constructor() {
    super();
    // Initializing instance fields

    // ... (your code)
  }
}

Further, those property "assignments" are actually no assignments but definitions:此外,那些属性“赋值”实际上不是赋值而是定义:

 class Setter { set property(value) { console.log("Value:", value); } } class A extends Setter { property = "from A"; } class B extends Setter { constructor() { super(); // Works effectively like class A: Object.defineProperty(this, "property", { value: "from B" }); } } class C extends Setter { constructor() { super(); this.property = "from C"; } } new A(); // No output, expected: "Value: from A" new B(); // No output, expected: "Value: from B" new C(); // Output: "Value: from C"

Variables变量

Identifiers are only resolved upon access, allowing this unintuitive code:标识符仅在访问时解析,允许这种不直观的代码:

 const logNumber = () => console.log(number); // References `number` before its declaration, but... const number = 0; logNumber(); //... it is resolved here, making it valid.

Also, identifiers are looked up from the nearest to the farthest lexical environment.此外,标识符是从最近到最远的词法环境中查找的。 This allows variable shadowing:这允许变量阴影:

 const variable = "initial-value"; { const variable = "other-value"; // Does NOT reassign, but *shadows* outer `variable`. console.log(variable); } console.log(variable);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM