阿里云主机折上折
  • 微信号
Current Site:Index > The constructor method translates this sentence into English.

The constructor method translates this sentence into English.

Author:Chuan Chen 阅读数:28819人阅读 分类: JavaScript

Basic Concepts of the constructor Method

The constructor method is the constructor function of a class in ECMAScript 6, used to create and initialize objects created by the class. When a class is instantiated using the new keyword, the constructor method is automatically called. This method is special in a class—it is neither a static method nor an instance method.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const person1 = new Person('Zhang San', 25);
console.log(person1.name); // Output: Zhang San

Characteristics of the constructor Method

The constructor method has several important features to note:

  1. Each class can have only one constructor method; otherwise, a SyntaxError will be thrown.
  2. If no constructor method is explicitly defined, JavaScript provides a default one.
  3. The constructor method returns the instance object (this) by default but can return other objects.
class Animal {
  // No explicit constructor defined
}

const animal = new Animal();
// JavaScript automatically adds the following constructor:
// constructor() {}

Parameters of the constructor Method

The constructor method can accept any number of parameters, which are passed when the class is instantiated:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  get area() {
    return this.height * this.width;
  }
}

const rect = new Rectangle(10, 20);
console.log(rect.area); // Output: 200

super Call in constructor

In an inherited class, the constructor method must call super() before using the this keyword:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Must call super first
    this.breed = breed;
  }
}

const myDog = new Dog('Wang Cai', 'Golden Retriever');
console.log(myDog.name); // Output: Wang Cai

Special Cases of constructor Return Values

Although constructor typically does not explicitly return a value, if it returns an object, that object will replace the default created instance:

class Foo {
  constructor() {
    return { bar: 'baz' };
  }
}

const foo = new Foo();
console.log(foo); // Output: { bar: 'baz' }
console.log(foo instanceof Foo); // Output: false

constructor vs. Prototype Methods

The constructor method differs from other class methods in that it is not added to the class's prototype:

class Example {
  constructor() {}
  method() {}
}

console.log(Example.prototype.method); // Output: [Function: method]
console.log(Example.prototype.constructor); // Output: [class Example]

Default Implementation of constructor

When a class does not explicitly define a constructor, the JavaScript engine automatically adds different default constructors based on the situation:

// Base class
class Base {}
// Equivalent to:
// class Base {
//   constructor() {}
// }

// Derived class
class Derived extends Base {}
// Equivalent to:
// class Derived extends Base {
//   constructor(...args) {
//     super(...args);
//   }
// }

Property Initialization in constructor

ES6 supports defining instance properties directly in the class, which can replace some initialization code in the constructor:

class OldWay {
  constructor() {
    this.counter = 0;
  }
}

class NewWay {
  counter = 0; // Class fields proposal
}

const old = new OldWay();
const newWay = new NewWay();
console.log(old.counter, newWay.counter); // Output: 0 0

constructor and Private Fields

ES2022 introduced private fields, which must be declared before the constructor and can only be accessed within the class:

class Counter {
  #count = 0; // Private field
  
  constructor(initialValue) {
    if (initialValue !== undefined) {
      this.#count = initialValue;
    }
  }
  
  increment() {
    this.#count++;
  }
  
  get value() {
    return this.#count;
  }
}

const counter = new Counter(5);
counter.increment();
console.log(counter.value); // Output: 6
console.log(counter.#count); // Error: Private field '#count' must be declared in an enclosing class

constructor and Static Initialization Blocks

ES2022 introduced static initialization blocks for complex static property initialization:

class MyClass {
  static x;
  static y;
  
  static {
    // Can access private static fields
    try {
      const result = someComplexInitialization();
      this.x = result.x;
      this.y = result.y;
    } catch {
      this.x = 0;
      this.y = 0;
    }
  }
  
  constructor() {
    // Instance initialization
  }
}

Performance Considerations for constructor

The performance of the constructor method affects object creation speed, especially in scenarios where instances are frequently created:

class Optimized {
  constructor(a, b) {
    this.a = a;
    this.b = b;
    // Avoid complex calculations in the constructor
  }
}

class Unoptimized {
  constructor(a, b) {
    this.a = a;
    this.b = b;
    this.c = this.computeSomething(); // Complex calculations in the constructor impact performance
  }
  
  computeSomething() {
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += Math.random();
    }
    return result;
  }
}

constructor and Inheritance Chains

In complex inheritance relationships, the calling order of constructor follows the prototype chain:

class A {
  constructor() {
    console.log('A constructor');
  }
}

class B extends A {
  constructor() {
    super();
    console.log('B constructor');
  }
}

class C extends B {
  constructor() {
    super();
    console.log('C constructor');
  }
}

const c = new C();
// Output:
// A constructor
// B constructor
// C constructor

constructor and new.target

The new.target meta-property can be used in the constructor to detect how the class was called:

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('Shape class cannot be instantiated directly');
    }
  }
}

class Circle extends Shape {}

// new Shape(); // Error: Shape class cannot be instantiated directly
new Circle(); // Works

constructor and Asynchronous Initialization

Although uncommon, the constructor can contain asynchronous operations, but note the case of returning a Promise:

class AsyncExample {
  constructor() {
    return (async () => {
      await someAsyncOperation();
      return this; // Must return this
    })();
  }
}

// Usage requires await
(async () => {
  const instance = await new AsyncExample();
})();

constructor and Proxies

The constructor call can be intercepted using a Proxy:

class Base {
  constructor(value) {
    this.value = value;
  }
}

const Handler = {
  construct(target, args) {
    console.log(`Creating instance with args: ${args}`);
    return new target(...args);
  }
};

const ProxiedClass = new Proxy(Base, Handler);
const instance = new ProxiedClass(42); // Output: Creating instance with args: 42

constructor and Decorators

Decorators can be used to modify the behavior of a class's constructor:

function logConstructor(target) {
  const original = target.prototype.constructor;
  
  target.prototype.constructor = function(...args) {
    console.log(`Creating instance with args: ${args}`);
    return original.apply(this, args);
  };
  
  return target;
}

@logConstructor
class MyClass {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
}

const obj = new MyClass(1, 2); // Output: Creating instance with args: 1,2

constructor and Class Expressions

The behavior of constructor in class expressions is consistent with that in class declarations:

const Person = class {
  constructor(name) {
    this.name = name;
  }
};

const person = new Person('Li Si');
console.log(person.name); // Output: Li Si

constructor and Generator Methods

Although the constructor itself cannot be a generator function, it can call generator methods:

class Sequence {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const seq = new Sequence(1, 5);
console.log([...seq]); // Output: [1, 2, 3, 4, 5]

constructor and Symbol.species

Symbol.species can override the constructor, useful for derived collection objects:

class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // Overrides the default constructor
  }
  
  constructor(...args) {
    super(...args);
  }
}

const myArray = new MyArray(1, 2, 3);
const mapped = myArray.map(x => x * 2);

console.log(mapped instanceof MyArray); // Output: false
console.log(mapped instanceof Array); // Output: true

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.