何时应使用JavaScript构造函数?对比普通函数的适用场景
我自己一直以来从没使用过类或构造函数,好奇有没有必须使用它们的场景。下面两段代码功能基本一致,一段用构造函数实现,另一段用普通函数实现:
使用构造函数的实现
function Food(foodName, foodPrice) { Currencies.call(this, foodPrice); this.category = 'food'; this.name = foodName; } function Currencies(usd) { this.usd = usd; this.won = +usd * 1000; } const pizza = new Food('pizza', 30); // { usd: 30, won: 30000, category: 'food', name: 'pizza' } const burger = new Food('burger', 10); // { usd: 10, won: 10000, category: 'food', name: 'burger' }
不使用构造函数的实现(普通工厂函数)
function food(foodName, foodPrice) { return { name: foodName, category: 'food', ...(currencies(foodPrice)) } } function currencies(usd) { return { usd, won: usd * 1000 } } const pizza = food('pizza', 30); // { usd: 30, won: 30000, category: 'food', name: 'pizza' } const burger = food('burger', 10); // { usd: 10, won: 10000, category: 'food', name: 'burger' }
我感觉不使用构造函数的写法更简洁,所以平时会尽量避免用构造函数。想问问大家的偏好和原因,以及什么时候会选择用构造函数而非普通函数?
首先得说,工厂函数(你写的普通函数)在简单场景下确实更清爽,很多开发者都会优先用,但构造函数(包括ES6的class,本质是构造函数的语法糖)在不少场景下是不可替代或者更合适的:
1. 方法复用,节省内存
如果你的对象需要共享方法,构造函数的优势就体现出来了。比如给Food加一个计算总价的方法:
// 构造函数写法:方法放在原型上,所有实例共享 Food.prototype.calculateTotal = function(taxRate) { return this.usd * (1 + taxRate); }; // 所有Food实例都用同一个方法,内存只占用一份
而如果用工厂函数,每次调用都要重新创建方法:
function food(foodName, foodPrice) { return { name: foodName, category: 'food', ...currencies(foodPrice), calculateTotal: function(taxRate) { // 每个对象都生成一个新函数,浪费内存 return this.usd * (1 + taxRate); } } }
虽然可以把方法抽出来单独定义,但这样就破坏了工厂函数的封装性,不如构造函数的原型链方式自然。
2. 类型识别与区分
用构造函数创建的实例可以通过instanceof判断类型:
console.log(pizza instanceof Food); // true
但工厂函数返回的都是普通Object,没法区分它是Food还是别的什么对象:
console.log(pizza instanceof Object); // true console.log(pizza instanceof food); // false,因为food不是构造函数
如果你的代码需要判断对象的具体类型(比如在函数里做类型校验),构造函数是更可靠的选择。
3. 复杂继承场景
当你需要实现类之间的继承时,构造函数配合原型链的方式更成熟、更易维护。比如想让Drink类继承Currencies,同时扩展自己的属性:
function Drink(drinkName, drinkPrice) { Currencies.call(this, drinkPrice); this.category = 'drink'; this.name = drinkName; } // 继承原型方法 Drink.prototype = Object.create(Currencies.prototype); Drink.prototype.constructor = Drink;
而用工厂函数实现继承,需要手动合并对象、处理属性覆盖,复杂场景下很容易出问题,代码也会变得臃肿。
4. 真正的封装(ES6+场景)
ES6的class是构造函数的语法糖,它支持私有字段(#开头的字段),这些字段只能在类内部访问,外部无法修改,实现了真正的封装:
class Food { #taxRate = 0.08; constructor(foodName, foodPrice) { this.usd = foodPrice; this.won = foodPrice * 1000; this.category = 'food'; this.name = foodName; } calculateTotal() { return this.usd * (1 + this.#taxRate); } }
而工厂函数返回的对象,所有属性都是公开的,没法做到这种严格的封装。
关于偏好
- 简单场景(比如只创建几个属性单一的对象):优先用工厂函数,代码简洁,不用考虑
new关键字和原型链。 - 复杂场景(需要方法复用、类型识别、继承、封装):用构造函数或ES6
class,代码结构更清晰,可维护性更高。
内容的提问来源于stack exchange,提问作者passionateLearner

