分析 JavaScript 中的 new.target 在 Class 和函数构造器中的作用，以及它如何影响继承和构造过程。

admin
Sat, 26 Jul 2025 18:27:13 +0000
Javascript, javascript, new, 构造

各位观众，掌声欢迎！今天咱们要聊聊 JavaScript 里一个有点神秘，但关键时刻能派上大用场的家伙：new.target。 开场白：new.target 是啥？ 你可能见过 this，知道它指向谁。但 new.target 是什么鬼？简单来说，new.target 就像一个侦探，专门负责追踪你是怎么被“new”出来的。它只在函数或者类的构造函数里有意义。 如果一个函数或者类是用 new 关键字调用的，new.target 就会指向这个函数或类本身。如果不是用 new 调用的，new.target 就是 undefined。 第一幕：函数构造器里的 new.target 在 ES5 及之前的年代，我们用函数来模拟类。那时候，防止函数被直接调用，确保只能通过 new 来创建实例，是个常见需求。new.target 出现之前，大家可能会用 this instanceof MyConstructor 这样的方式来判断。现在，有了 new.target，就优雅多了。 function Person(name) { if (!new.target) { throw new Error("Per …

继续阅读“分析 JavaScript 中的 new.target 在 Class 和函数构造器中的作用，以及它如何影响继承和构造过程。”

The post 分析 JavaScript 中的 new.target 在 Class 和函数构造器中的作用，以及它如何影响继承和构造过程。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位观众，掌声欢迎！今天咱们要聊聊 JavaScript 里一个有点神秘，但关键时刻能派上大用场的家伙：new.target。

开场白：new.target 是啥？

你可能见过 this，知道它指向谁。但 new.target 是什么鬼？简单来说，new.target 就像一个侦探，专门负责追踪你是怎么被“new”出来的。它只在函数或者类的构造函数里有意义。

如果一个函数或者类是用 new 关键字调用的，new.target 就会指向这个函数或类本身。如果不是用 new 调用的，new.target 就是 undefined。

第一幕：函数构造器里的 new.target

在 ES5 及之前的年代，我们用函数来模拟类。那时候，防止函数被直接调用，确保只能通过 new 来创建实例，是个常见需求。new.target 出现之前，大家可能会用 this instanceof MyConstructor 这样的方式来判断。现在，有了 new.target，就优雅多了。

function Person(name) {
  if (!new.target) {
    throw new Error("Person 必须用 'new' 调用");
  }
  this.name = name;
}

// 正确用法
const john = new Person("John");
console.log(john.name); // 输出 "John"

// 错误用法
try {
  const jane = Person("Jane"); // 直接调用，会报错
} catch (error) {
  console.error(error.message); // 输出 "Person 必须用 'new' 调用"
}

在这个例子里，如果 Person 函数不是通过 new 调用的，new.target 就是 undefined，if (!new.target) 就会触发错误，阻止了函数的直接调用。

第二幕：Class 里的 new.target

ES6 引入了 class 语法，new.target 在 class 构造函数里也一样有效。但它还有更高级的用法，尤其是在抽象类和继承方面。

class Animal {
  constructor() {
    if (new.target === Animal) {
      throw new Error("Animal 是一个抽象类，不能直接实例化");
    }
  }

  speak() {
    throw new Error("speak 方法必须在子类中实现");
  }
}

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

  speak() {
    return "Woof!";
  }
}

// 错误用法
try {
  const animal = new Animal(); // 报错：Animal 是一个抽象类，不能直接实例化
} catch (error) {
  console.error(error.message);
}

// 正确用法
const dog = new Dog("Buddy");
console.log(dog.speak()); // 输出 "Woof!"

这里，Animal 类被设计成一个抽象类，不能直接实例化。new.target === Animal 确保了这一点。只有子类（比如 Dog）才能被 new 出来。

第三幕：new.target 与继承

new.target 在继承中扮演着重要的角色，尤其是在多层继承的情况下。它能准确地告诉你，最终是谁被 new 出来的。

class Base {
  constructor() {
    console.log("Base constructor, new.target:", new.target.name);
  }
}

class Derived extends Base {
  constructor() {
    super();
    console.log("Derived constructor, new.target:", new.target.name);
  }
}

class FurtherDerived extends Derived {
  constructor() {
    super();
    console.log("FurtherDerived constructor, new.target:", new.target.name);
  }
}

const fd = new FurtherDerived();
// 输出:
// Base constructor, new.target: FurtherDerived
// Derived constructor, new.target: FurtherDerived
// FurtherDerived constructor, new.target: FurtherDerived

可以看到，即使在 Base 类的构造函数里，new.target 仍然指向最终被 new 出来的 FurtherDerived 类。这使得我们可以在基类中，根据最终的派生类执行不同的逻辑。

第四幕：new.target 的高级用法：工厂模式

new.target 还可以用于实现更灵活的工厂模式。

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error("Shape 是一个基类，不能直接实例化");
    }
  }

  static create(type, ...args) {
    switch (type) {
      case "circle":
        return new Circle(...args);
      case "square":
        return new Square(...args);
      default:
        throw new Error("不支持的形状类型");
    }
  }

  draw() {
    throw new Error("draw 方法必须在子类中实现");
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  draw() {
    return `绘制一个半径为 ${this.radius} 的圆形`;
  }
}

class Square extends Shape {
  constructor(side) {
    super();
    this.side = side;
  }

  draw() {
    return `绘制一个边长为 ${this.side} 的正方形`;
  }
}

const circle = Shape.create("circle", 5);
console.log(circle.draw()); // 输出 "绘制一个半径为 5 的圆形"

const square = Shape.create("square", 10);
console.log(square.draw()); // 输出 "绘制一个边长为 10 的正方形"

在这个例子里，Shape 类提供了一个静态方法 create，根据传入的类型创建不同的形状实例。虽然 Shape 本身不能直接实例化，但可以通过 create 方法间接创建子类的实例。

第五幕：new.target 与元编程

new.target 还可以结合 Proxy 等元编程特性，实现更高级的功能，比如控制类的实例化过程。

function classFactory(className, baseClass, properties) {
  const handler = {
    construct(target, args, newTarget) {
      // 在构造函数调用前进行拦截
      console.log(`正在创建 ${className} 的实例...`);
      const instance = Reflect.construct(target, args, newTarget);
      // 在构造函数调用后进行拦截
      console.log(`${className} 的实例创建完成`);
      return instance;
    }
  };

  const proxyClass = new Proxy(baseClass, handler);

  return proxyClass;
}

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

const ProxiedMyClass = classFactory("MyClass", MyClass);

const instance = new ProxiedMyClass("Alice"); // 输出拦截信息
console.log(instance.name); // 输出 Alice

这个例子中，classFactory 函数使用 Proxy 拦截了 MyClass 的构造过程，可以在实例创建前后执行自定义逻辑。

new.target 的总结：

咱们来总结一下 new.target 的作用：

防止函数或抽象类被直接调用： 确保函数只能通过 new 来创建实例，或者阻止抽象类被直接实例化。

在继承中确定最终的派生类： 即使在基类的构造函数里，也能知道最终是谁被 new 出来的。

实现更灵活的工厂模式： 可以根据不同的条件创建不同的实例，而无需直接使用 new 关键字。

结合元编程进行更高级的控制： 可以拦截类的实例化过程，实现更复杂的逻辑。

表格总结:

用途 | 说明 | 示例
防止函数/抽象类直接调用 | 确保函数只能通过 new 调用，或者阻止抽象类被直接实例化。 | javascript function Person(name) { if (!new.target) { throw new Error("Person 必须用 'new' 调用"); } this.name = name; } class Animal { constructor() { if (new.target === Animal) { throw new Error("Animal 是一个抽象类"); } } }
在继承中确定最终派生类 | 即使在基类的构造函数中，也能知道最终哪个类被实例化。 | javascript class Base { constructor() { console.log("Base constructor, new.target:", new.target.name); } } class Derived extends Base {} const d = new Derived(); // Base constructor, new.target: Derived
实现工厂模式 | 根据类型创建不同类的实例，无需直接使用 new。 | javascript class Shape { static create(type) { if (type === 'circle') return new Circle(); if (type === 'square') return new Square(); } }
结合元编程进行高级控制 | 拦截类的实例化过程，执行自定义逻辑。 | javascript const handler = { construct(target, args) { console.log('Creating instance'); return new target(...args); } }; const MyClass = new Proxy(class {}, handler); new MyClass(); // 输出: Creating instance

结尾：

new.target 虽然平时不显山不露水，但在某些场景下，却是解决问题的关键。掌握了它，你的 JavaScript 功力就能更上一层楼。希望今天的讲解对你有所帮助！下次再见！