跳到主要内容

JavaScript 构造函数

在JavaScript的对象世界里,构造函数是一种特殊的函数,用于创建新对象并初始化其属性和方法。构造函数就像一个"对象工厂",能够批量生产具有相同结构的对象,帮助我们避免重复编写相似的代码。

什么是构造函数?

构造函数本质上是一个普通的JavaScript函数,但它有两个重要的特点:

  1. 函数名通常以大写字母开头(这是一种编程约定,帮助识别它是构造函数而非普通函数)
  2. 使用new关键字来调用该函数以创建新对象
备注

构造函数是创建多个相似对象的"蓝图"或"模板",这种面向对象的编程方式在开发复杂应用时非常有用。

基础语法

js
// 定义构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
return "你好,我是" + this.name + ",今年" + this.age + "岁";
};
}

// 使用构造函数创建对象
const person1 = new Person("张三", 25);
const person2 = new Person("李四", 30);

// 调用对象方法
console.log(person1.sayHello()); // 输出: 你好,我是张三,今年25岁
console.log(person2.sayHello()); // 输出: 你好,我是李四,今年30岁

工作原理详解

当我们使用new关键字调用构造函数时,JavaScript会执行以下步骤:

  1. 创建一个新的空对象({}
  2. 将这个新对象的__proto__属性链接到构造函数的prototype属性
  3. 将构造函数内部的this绑定到新创建的对象
  4. 执行构造函数内部的代码(通常用于添加属性和方法)
  5. 如果构造函数没有返回其他对象,则自动返回这个新创建的对象

构造函数与普通函数的区别

构造函数和普通函数的主要区别在于调用方式和作用:

js
// 作为构造函数使用
function Car(make, model) {
this.make = make;
this.model = model;
}

const myCar = new Car("丰田", "卡罗拉");
console.log(myCar.make); // 输出: 丰田

// 如果不使用new关键字(作为普通函数调用)
const notACar = Car("本田", "雅阁");
console.log(notACar); // 输出: undefined
// this指向全局对象(在浏览器中是window)
console.log(window.make); // 输出: 本田(在浏览器环境中)
警告

如果忘记使用new关键字,构造函数会作为普通函数执行,this会指向全局对象(浏览器中是window),可能导致意外的全局变量污染。

构造函数的原型(prototype)

每个构造函数都有一个prototype属性,指向一个对象。当使用构造函数创建实例时,实例会继承构造函数prototype上的属性和方法。

这样可以让所有实例共享方法,节省内存:

js
function Student(name, grade) {
this.name = name;
this.grade = grade;
}

// 在原型上定义方法
Student.prototype.introduce = function() {
return `我叫${this.name},我在${this.grade}年级`;
};

const student1 = new Student("小明", "三");
const student2 = new Student("小红", "四");

// 两个实例共享同一个方法
console.log(student1.introduce()); // 输出: 我叫小明,我在三年级
console.log(student2.introduce()); // 输出: 我叫小红,我在四年级
console.log(student1.introduce === student2.introduce); // 输出: true
提示

将方法定义在原型上而非直接在构造函数内部定义,可以提高内存效率。每个实例会共享原型上的方法,而不是各自创建方法副本。

检查对象类型

可以使用instanceof运算符检查一个对象是否是某个构造函数的实例:

js
function Animal(name) {
this.name = name;
}

const cat = new Animal("猫");

console.log(cat instanceof Animal); // 输出: true
console.log(cat instanceof Object); // 输出: true
console.log(cat instanceof Array); // 输出: false

构造函数的实际应用场景

1. 管理用户数据

js
function User(username, email, role = "user") {
this.username = username;
this.email = email;
this.role = role;
this.createdAt = new Date();
this.isActive = true;

this.deactivate = function() {
this.isActive = false;
};

this.promote = function(newRole) {
this.role = newRole;
};
}

// 创建管理员和普通用户
const admin = new User("admin", "admin@example.com", "admin");
const normalUser = new User("user123", "user@example.com");

// 使用对象方法
normalUser.deactivate();
console.log(normalUser.isActive); // 输出: false

admin.promote("super-admin");
console.log(admin.role); // 输出: super-admin

2. 创建商品信息系统

js
function Product(name, price, category) {
this.name = name;
this.price = price;
this.category = category;
this.inStock = true;

this.applyDiscount = function(percentage) {
const discount = (percentage / 100) * this.price;
return this.price - discount;
};

this.markOutOfStock = function() {
this.inStock = false;
};
}

const phone = new Product("智能手机", 2999, "电子产品");
const book = new Product("JavaScript入门", 59, "图书");

console.log(phone.applyDiscount(15)); // 输出: 2549.15
book.markOutOfStock();
console.log(book.inStock); // 输出: false

构造函数的最佳实践

  1. 始终使用大写字母开头命名构造函数 这有助于区分普通函数和构造函数

  2. 总是使用new关键字调用构造函数 为了避免忘记使用new,可以在构造函数内添加防御性代码:

    js
    function Person(name) {
    if (!(this instanceof Person)) {
    return new Person(name);
    }
    this.name = name;
    }
  3. 方法定义放在原型上

    js
    function Car(make, model) {
    this.make = make;
    this.model = model;
    }

    // 这种方式更高效
    Car.prototype.getInfo = function() {
    return `${this.make} ${this.model}`;
    };
  4. 使用Object.create(null)避免原型污染

    js
    function SafeConstructor(data) {
    this.data = Object.create(null); // 创建没有原型的纯净对象
    this.data.value = data;
    }

总结

JavaScript构造函数是创建具有相同结构的多个对象的有效方式。它们是JavaScript面向对象编程的基础,通过new关键字和函数内的this关键字,我们可以创建自定义对象类型并实例化它们。

掌握构造函数对深入理解JavaScript的原型继承和面向对象编程至关重要。结合原型系统,构造函数能够创建功能强大且内存高效的对象结构。

练习题

  1. 创建一个Book构造函数,包含书名(title)、作者(author)和是否已读(isRead)属性,以及标记为已读的方法(markAsRead)。
  2. 创建一个Circle构造函数,接收半径参数,并有计算面积和周长的方法。
  3. 编写一个BankAccount构造函数,包含余额、存款和取款方法,确保账户不会出现负值。

进一步学习资源

  • JavaScript的原型继承
  • ES6类语法(构造函数的现代替代方案)
  • 设计模式中的工厂模式与构造函数模式

构造函数只是面向对象编程的开始,随着你的技能提升,可以探索ES6类、继承和更复杂的对象组织方式。