阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > constructor方法

constructor方法

作者:陈川 阅读数:28818人阅读 分类: JavaScript

constructor方法的基本概念

constructor方法是ECMAScript 6中类(class)的构造函数,用于创建和初始化类创建的对象。当通过new关键字实例化一个类时,constructor方法会被自动调用。这个方法在类中是特殊的,它既不是静态方法也不是实例方法。

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

const person1 = new Person('张三', 25);
console.log(person1.name); // 输出: 张三

constructor方法的特性

constructor方法有几个重要特性需要注意:

  1. 每个类只能有一个constructor方法,否则会抛出SyntaxError
  2. 如果没有显式定义constructor方法,JavaScript会提供一个默认的constructor
  3. constructor方法默认返回实例对象(this),但可以返回其他对象
class Animal {
  // 没有显式定义constructor
}

const animal = new Animal();
// JavaScript会自动添加如下constructor:
// constructor() {}

constructor方法的参数

constructor方法可以接受任意数量的参数,这些参数在实例化类时传递:

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); // 输出: 200

constructor中的super调用

在继承的类中,constructor方法必须先调用super()才能使用this关键字:

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

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 必须先调用super
    this.breed = breed;
  }
}

const myDog = new Dog('旺财', '金毛');
console.log(myDog.name); // 输出: 旺财

constructor返回值的特殊情况

虽然constructor通常不显式返回值,但如果返回一个对象,则该对象会替代默认创建的实例:

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

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

constructor与原型方法

constructor方法与其他类方法不同,它不会被添加到类的原型上:

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

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

constructor的默认实现

当类没有显式定义constructor时,JavaScript引擎会根据情况自动添加不同的默认constructor:

// 基类
class Base {}
// 相当于:
// class Base {
//   constructor() {}
// }

// 派生类
class Derived extends Base {}
// 相当于:
// class Derived extends Base {
//   constructor(...args) {
//     super(...args);
//   }
// }

constructor中的属性初始化

ES6支持直接在类中定义实例属性,这可以替代部分constructor中的初始化代码:

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

class NewWay {
  counter = 0; // 类字段提案
}

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

constructor与私有字段

ES2022引入了私有字段,这些字段必须在constructor之前声明,且只能在类内部访问:

class Counter {
  #count = 0; // 私有字段
  
  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); // 输出: 6
console.log(counter.#count); // 报错: Private field '#count' must be declared in an enclosing class

constructor与静态初始化块

ES2022引入了静态初始化块,可以用于复杂的静态属性初始化:

class MyClass {
  static x;
  static y;
  
  static {
    // 可以访问私有静态字段
    try {
      const result = someComplexInitialization();
      this.x = result.x;
      this.y = result.y;
    } catch {
      this.x = 0;
      this.y = 0;
    }
  }
  
  constructor() {
    // 实例初始化
  }
}

constructor的性能考虑

constructor方法的性能会影响对象的创建速度,特别是在频繁创建实例的场景下:

class Optimized {
  constructor(a, b) {
    this.a = a;
    this.b = b;
    // 避免在constructor中做复杂计算
  }
}

class Unoptimized {
  constructor(a, b) {
    this.a = a;
    this.b = b;
    this.c = this.computeSomething(); // 复杂计算放在constructor中会影响性能
  }
  
  computeSomething() {
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += Math.random();
    }
    return result;
  }
}

constructor与继承链

在复杂的继承关系中,constructor的调用顺序遵循原型链:

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();
// 输出:
// A constructor
// B constructor
// C constructor

constructor与new.target

new.target元属性可以在constructor中检测类是如何被调用的:

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('Shape类不能直接实例化');
    }
  }
}

class Circle extends Shape {}

// new Shape(); // 报错: Shape类不能直接实例化
new Circle(); // 正常

constructor与异步初始化

虽然不常见,但constructor可以包含异步操作,不过需要注意返回Promise的情况:

class AsyncExample {
  constructor() {
    return (async () => {
      await someAsyncOperation();
      return this; // 必须返回this
    })();
  }
}

// 使用时需要await
(async () => {
  const instance = await new AsyncExample();
})();

constructor与代理

可以通过Proxy来拦截constructor的调用:

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

const Handler = {
  construct(target, args) {
    console.log(`创建实例,参数: ${args}`);
    return new target(...args);
  }
};

const ProxiedClass = new Proxy(Base, Handler);
const instance = new ProxiedClass(42); // 输出: 创建实例,参数: 42

constructor与装饰器

使用装饰器可以修改类的constructor行为:

function logConstructor(target) {
  const original = target.prototype.constructor;
  
  target.prototype.constructor = function(...args) {
    console.log(`创建实例,参数: ${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); // 输出: 创建实例,参数: 1,2

constructor与类表达式

类表达式中的constructor与类声明中的行为一致:

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

const person = new Person('李四');
console.log(person.name); // 输出: 李四

constructor与生成器方法

虽然constructor本身不能是生成器函数,但可以在其中调用生成器方法:

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]); // 输出: [1, 2, 3, 4, 5]

constructor与Symbol.species

可以通过Symbol.species覆盖constructor,用于派生集合对象:

class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // 覆盖默认的constructor
  }
  
  constructor(...args) {
    super(...args);
  }
}

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

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

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌