构造器模式(Constructor)与传统类的区别
构造器模式(Constructor)与传统类在JavaScript中都是创建对象的方式,但它们的实现机制和使用场景存在显著差异。构造器模式依赖于函数和原型链,而传统类基于ES6的class
语法糖,虽然最终都通过原型继承实现,但写法、功能扩展和细节处理上有所不同。
构造器模式的核心实现
构造器模式通过普通函数模拟类的行为,函数内部通过this
绑定实例属性,方法则挂载到函数的原型对象上。这种模式显式依赖原型链,需要手动处理原型关系:
function Animal(name) {
this.name = name;
this.speed = 0;
}
Animal.prototype.run = function(speed) {
this.speed += speed;
console.log(`${this.name} runs with speed ${this.speed}`);
};
const rabbit = new Animal('White Rabbit');
rabbit.run(5); // White Rabbit runs with speed 5
关键特征包括:
- 构造函数首字母通常大写
- 实例属性通过
this
动态绑定 - 方法必须显式定义在
prototype
上 - 继承需要通过
Parent.call(this)
和手动设置子类原型
传统类的语法结构
ES6的class
提供了更接近传统面向对象语言的语法,但底层仍是原型继承的语法糖:
class Animal {
constructor(name) {
this.name = name;
this.speed = 0;
}
run(speed) {
this.speed += speed;
console.log(`${this.name} runs with speed ${this.speed}`);
}
}
const rabbit = new Animal('Black Rabbit');
rabbit.run(10); // Black Rabbit runs with speed 10
与传统构造器相比:
- 方法直接定义在类体内
- 构造函数使用固定
constructor
名称 - 支持
static
静态方法 - 属性初始化器提案允许直接在类顶层声明属性
继承实现的差异对比
构造器模式的继承需要手动组合原型链和构造函数调用:
function Rabbit(name, earLength) {
Animal.call(this, name);
this.earLength = earLength;
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
Rabbit.prototype.jump = function() {
console.log(`${this.name} jumps!`);
};
而类继承通过extends
和super
简化流程:
class Rabbit extends Animal {
constructor(name, earLength) {
super(name);
this.earLength = earLength;
}
jump() {
console.log(`${this.name} jumps!`);
}
}
关键区别点:
- 类继承强制要求
super()
先于this
访问 - 类方法默认不可枚举
- 子类
__proto__
指向父类(实现静态继承)
方法定义的内部差异
构造器模式中,方法添加到原型上是可配置、可枚举的:
Object.getOwnPropertyDescriptor(Animal.prototype, 'run');
// {value: ƒ, writable: true, enumerable: true, configurable: true}
而类方法默认是非枚举的:
class Animal {
run() {}
}
Object.getOwnPropertyDescriptor(Animal.prototype, 'run');
// {value: ƒ, writable: true, enumerable: false, configurable: true}
静态成员的处理方式
构造器模式的静态成员直接附加到函数对象上:
Animal.compare = function(a, b) {
return a.speed - b.speed;
};
类语法提供专门的static
关键字:
class Animal {
static compare(a, b) {
return a.speed - b.speed;
}
}
私有字段的支持情况
现代JavaScript中,类支持实验性的私有字段:
class Animal {
#secret = 'hidden';
getSecret() {
return this.#secret;
}
}
而构造器模式需要通过闭包或WeakMap模拟私有性:
const Animal = (function() {
const secrets = new WeakMap();
function Animal(name) {
secrets.set(this, { secret: 'hidden' });
this.name = name;
}
Animal.prototype.getSecret = function() {
return secrets.get(this).secret;
};
return Animal;
})();
类型检测的不同表现
构造器模式实例通过instanceof
检查原型链:
rabbit instanceof Animal; // true
类实例除了instanceof
还暴露内部[[IsClassConstructor]]
标记:
class Animal {}
typeof Animal; // "function"
Animal.toString(); // "class Animal {...}"
提升行为的区别
函数声明会提升,构造器模式可用在定义前:
new Animal('Early Bird'); // 正常运行
function Animal() {}
类声明存在暂时性死区:
new Animal('Early Bird'); // ReferenceError
class Animal {}
设计模式中的应用场景
构造器模式更灵活,适合需要动态修改原型的场景:
function DynamicClass() {}
// 运行时动态添加方法
if (condition) {
DynamicClass.prototype.method = function() {};
}
类更适合结构固定的场景,尤其是需要明确继承关系时:
class Component {
// 明确的接口定义
}
class Button extends Component {
// 清晰的继承层次
}
性能与内存考量
现代JavaScript引擎对两种形式优化程度相当。但构造器模式可能:
- 原型修改会触发隐藏类变化
- 动态添加方法影响内联缓存
- 大型项目中更难静态分析
类语法由于结构固定,更容易被引擎优化:
// 引擎可以预先确定类结构
class FixedStructure {
method1() {}
method2() {}
}
元编程能力的区别
构造器函数作为普通函数,可以直接用apply/call
:
function Person(name) {
this.name = name;
}
const obj = {};
Person.call(obj, 'Alice');
类构造函数调用时检查new.target
,直接调用会报错:
class Person {
constructor(name) {
if (!new.target) throw new Error();
this.name = name;
}
}
Person.call(obj, 'Bob'); // Error
装饰器提案的兼容性
当前装饰器提案仅支持类语法:
@decorator
class Animal {
@methodDecorator
run() {}
}
构造器模式需要使用高阶函数模拟装饰:
function decoratedConstructor(ctor) {
return function(...args) {
// 装饰逻辑
return new ctor(...args);
};
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn