源本科技 | 码上会

JavaScript 构造方法

2026/01/13
14
0

学习目标

  • 理解 JavaScript 中构造方法的核心作用与工作机制

  • 掌握两种主要的构造方法定义方式:函数构造器与类构造器

  • 学会使用 new 关键字创建对象实例

  • 了解 Object.create()、工厂函数等替代对象创建方式及其适用场景

  • 掌握为构造方法设置默认参数、使用原型共享方法、实现继承等高级技巧


什么是构造方法

在 JavaScript 中,构造方法是一种特殊函数,用于创建并初始化对象。它通过 new 关键字调用,自动完成以下操作:

  1. 创建一个新空对象

  2. this 绑定到该新对象

  3. 执行构造函数体内的代码(通常用于设置属性)

  4. 隐式返回新创建的对象

构造方法使得我们可以批量创建结构一致但数据不同的对象实例。


构造方法的定义方式

1. 函数构造器(ES6 之前)

这是早期 JavaScript 实现面向对象的主要方式:

function Car(brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
    this.getInfo = function() {
        return `${this.year} 年款 ${this.brand} ${this.model}`;
    };
}

const myCar = new Car("丰田", "凯美瑞", 2020);
console.log(myCar.getInfo()); // 输出:2020 年款 丰田 凯美瑞

特点

  • 函数名首字母通常大写(如 Car),表示它是构造函数

  • 使用 this 为新对象添加属性和方法

  • 每次创建实例时,方法都会被重新定义(内存效率较低)

2. 类构造器(ES6 以后)

ES6 引入了更清晰的 class 语法:

class Car {
    constructor(brand, model, year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
    }

    getInfo() {
        return `${this.year} 年款 ${this.brand} ${this.model}`;
    }
}

const myCar = new Car("本田", "思域", 2022);
console.log(myCar.getInfo()); // 输出:2022 年款 本田 思域

优势

  • 语法更简洁、结构更清晰

  • 方法自动绑定到原型(prototype),节省内存

  • 更接近传统 OOP 语言风格,易于理解


new 关键字

无论使用函数还是类,new 都是创建实例的关键:

// 使用函数构造器
function Student(name, age) {
    this.name = name;
    this.age = age;
}
const stu1 = new Student("李明", 21);

// 使用类构造器
class Teacher {
    constructor(name, subject) {
        this.name = name;
        this.subject = subject;
    }
}
const tea1 = new Teacher("王老师", "数学");

console.log(stu1); // Student { name: '李明', age: 21 }
console.log(tea1); // Teacher { name: '王老师', subject: '数学' }

new 的核心作用是将普通函数 / 类转换为对象生成器


其他对象创建方式

1. Object.create()

基于原型的复制

该方法不调用构造函数,而是直接以一个对象为原型创建新对象:

const baseUser = {
    role: "user",
    login() {
        console.log(`${this.name} 已登录`);
    }
};

const user1 = Object.create(baseUser);
user1.name = "张三";
user1.login(); // 输出:张三 已登录

console.log(user1.role); // 输出:user(从原型继承)

适用于无需初始化逻辑、只需继承已有对象行为的场景。

2. 工厂函数

返回对象的普通函数,无需 new

function createUser(name, email) {
    return {
        name: name,
        email: email,
        greet() {
            console.log(`你好,${this.name}!`);
        }
    };
}

const user1 = createUser("赵六", "zhao@example.com");
user1.greet(); // 输出:你好,赵六!

优点

  • 避免 this 绑定问题

  • 可封装私有变量(通过闭包)

  • 更灵活,可返回不同类型的对象


构造方法的高级用法

1. 默认参数值

为参数提供默认值,增强健壮性:

function Person(name = "匿名", age = 0) {
    this.name = name;
    this.age = age;
}

const p1 = new Person("刘洋", 28); // 正常传参
const p2 = new Person("陈晨");     // age 使用默认值 0
const p3 = new Person();           // 全部使用默认值

console.log(p1); // Person { name: '刘洋', age: 28 }
console.log(p2); // Person { name: '陈晨', age: 0 }
console.log(p3); // Person { name: '匿名', age: 0 }

2. 将方法定义在原型上

优化内存

避免每次创建实例都重复定义方法:

function Animal(species) {
    this.species = species;
}

// 方法定义在原型上,所有实例共享
Animal.prototype.speak = function() {
    console.log(`这是一只 ${this.species}`);
};

const cat = new Animal("猫");
const dog = new Animal("狗");

cat.speak(); // 输出:这是一只 猫
dog.speak(); // 输出:这是一只 狗

// 验证是否共享同一个方法
console.log(cat.speak === dog.speak); // true

这是函数构造器中提升性能的关键技巧。

3. 构造方法中的继承

使用 extendssuper 实现类继承:

class Vehicle {
    constructor(type) {
        this.type = type;
    }

    start() {
        console.log(`${this.type} 启动了`);
    }
}

class Bike extends Vehicle {
    constructor(brand, color) {
        super("自行车"); // 调用父类构造函数
        this.brand = brand;
        this.color = color;
    }

    describe() {
        return `${this.color} 的 ${this.brand} 自行车`;
    }
}

const myBike = new Bike("捷安特", "蓝色");
myBike.start();      // 输出:自行车 启动了
console.log(myBike.describe()); // 输出:蓝色 的 捷安特 自行车

构造方法的优势

优势

说明

代码复用

可创建多个结构相同的对象实例

封装性

初始化逻辑集中,保持代码整洁

参数化初始化

支持传入不同参数创建不同状态的对象

支持继承

可构建类层次结构,实现代码扩展

可维护性强

对象创建逻辑统一,便于调试与修改


重点总结

  • 构造方法是创建和初始化对象的核心机制

  • ES6 的 class 语法是对传统函数构造器的语法糖,更易读

  • new 关键字负责实例化过程,绑定 this 并返回对象

  • 方法应尽量定义在原型上(函数构造器)或类体内(ES6 类),避免重复创建

  • 工厂函数和 Object.create() 提供了不依赖 new 的替代方案

  • 默认参数、继承、原型共享等技巧可显著提升代码质量与性能


思考题

  1. 如果忘记在调用构造函数时使用 new(如 let obj = Person("张三", 20)),会发生什么?如何避免此类错误?

  2. 在函数构造器中,为什么推荐将方法定义在 prototype 上而不是构造函数内部?请从内存和性能角度分析。

  3. 比较工厂函数与类构造器的优缺点,在什么场景下应优先选择工厂函数?