1. 构造函数与实例方法的定义策略
- 构造函数内部定义方法:
- 适用场景:当方法需要访问特定实例的属性,且这些属性在对象创建时确定,并且每个实例的该方法行为需要依赖自身独特的属性值时,适合在构造函数内部定义。例如,
Vehicle
构造函数可能接受一个id
参数,每个Vehicle
实例都有自己唯一的id
,如果有一个getInstanceId
方法需要返回该实例的id
,就可以在构造函数内部定义。
- 示例代码:
function Vehicle(id) {
this.id = id;
this.getInstanceId = function() {
return this.id;
};
}
- 优点:每个实例的方法都是独立的,相互之间不会干扰,对于需要操作实例独有的数据非常方便。
- 缺点:每个实例都会创建该方法的副本,会占用更多的内存空间。
- 原型上定义方法:
- 适用场景:当方法的逻辑不依赖于特定实例的属性,或者所有实例对于该方法的行为是一致的情况下,适合在原型上定义。例如,
Vehicle
的move
方法,所有车辆的移动逻辑可能大致相同,就可以定义在原型上。
- 示例代码:
function Vehicle() {}
Vehicle.prototype.move = function() {
console.log('The vehicle is moving');
};
- 优点:方法只存在一份,所有实例共享,节省内存空间。
- 缺点:如果不小心在原型方法中修改了共享的原型属性,可能会影响到所有实例。
2. 设计具有继承关系的构造函数
function Vehicle() {
this.wheels = 4;
}
Vehicle.prototype.move = function() {
console.log('The vehicle is moving');
};
function Car() {
Vehicle.call(this); // 借用Vehicle的构造函数,初始化实例属性
this.color = 'default';
}
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;
Car.prototype.drive = function() {
console.log('The car is driving');
};
- 解释:首先在
Car
构造函数内部通过Vehicle.call(this)
调用Vehicle
构造函数,确保Car
实例拥有Vehicle
实例的属性。然后通过Object.create(Vehicle.prototype)
创建一个新的对象,其原型为Vehicle.prototype
,并将其赋值给Car.prototype
,最后修正Car.prototype.constructor
为Car
。这样Car
就继承了Vehicle
的属性和方法,并且可以添加自己独特的方法。
Truck
继承Vehicle
:
function Truck() {
Vehicle.call(this);
this.loadCapacity = 0;
}
Truck.prototype = Object.create(Vehicle.prototype);
Truck.prototype.constructor = Truck;
Truck.prototype.load = function(weight) {
this.loadCapacity += weight;
console.log(`The truck is loaded with ${weight} tons`);
};
3. 多重继承场景下避免常见问题
- 避免原型链混乱:
- 策略:不使用传统的JavaScript多重继承方式(因为会导致原型链混乱),可以使用混入(mixin)模式。例如,如果
Truck
需要额外的功能,如hasGPS
,可以创建一个包含hasGPS
功能的对象,并将其方法混入到Truck
中。
- 示例代码:
const GPSMixin = {
hasGPS: function() {
return true;
}
};
function Truck() {
Vehicle.call(this);
this.loadCapacity = 0;
}
Truck.prototype = Object.create(Vehicle.prototype);
Truck.prototype.constructor = Truck;
Truck.prototype.load = function(weight) {
this.loadCapacity += weight;
console.log(`The truck is loaded with ${weight} tons`);
};
// 混入GPS功能
Object.assign(Truck.prototype, GPSMixin);
- 解释:
Object.assign
方法将GPSMixin
对象的属性和方法复制到Truck.prototype
上,这样Truck
实例就拥有了hasGPS
方法,而不会干扰原型链的正常结构。
- 避免内存泄漏:
- 策略:确保在对象销毁时,清除所有的事件绑定、定时器等可能导致内存泄漏的引用。例如,如果
Car
对象绑定了一个全局事件,在Car
实例销毁时需要移除该事件绑定。
- 示例代码:
function Car() {
this.color = 'default';
document.addEventListener('click', this.handleClick.bind(this));
}
Car.prototype.handleClick = function() {
console.log('Car instance clicked');
};
Car.prototype.destroy = function() {
document.removeEventListener('click', this.handleClick.bind(this));
};
- 解释:在
Car
构造函数中绑定了click
事件,在destroy
方法中移除该事件绑定,当Car
实例不再使用时调用destroy
方法,可以避免因事件绑定导致的内存泄漏。