call和apply可复用父构造函数的属性初始化逻辑,但不建立原型链,需配合object.setprototypeof等手动设置原型才能继承方法;现代推荐使用class+extends语法。

在 JavaScript 中,call 和 apply 可以让子构造函数“借用”父构造函数的初始化逻辑,实现属性的继承复用——但注意:它们只负责执行父构造函数体内的代码(如给 this 赋值),**不建立原型链**,因此不能替代 Object.setPrototypeOf 或 extends 来实现方法继承。
用 call 实现构造函数内属性的复用
call 允许你显式指定 this 并逐个传参,适合参数数量固定、明确的场景:
- 在子构造函数中调用
Parent.call(this, arg1, arg2),相当于把父构造函数的初始化逻辑“运行在子实例上” - 这样子实例就拥有了父类定义的实例属性(如
this.name、this.age) - 示例:
function Animal(name, age) {
this.name = name;
this.age = age;
}
function Dog(name, age, breed) {
Animal.call(this, name, age); // 复用初始化逻辑
this.breed = breed;
}
const dog = new Dog('旺财', 3, '柴犬');
console.log(dog.name); // '旺财'
console.log(dog.breed); // '柴犬'
用 apply 实现参数不确定时的复用
当子构造函数接收的参数个数不固定,或你想把一个数组/类数组作为参数列表传入父构造函数时,用 apply 更自然:
- 语法是
Parent.apply(this, argsArray) - 常见于封装或代理构造函数,比如写一个通用的子类初始化器
- 示例:
function Person(name, age, city) {
this.name = name;
this.age = age;
this.city = city;
}
function Student(name, age, city, grade, school) {
// 把前三个参数传给 Person,其余自行处理
Person.apply(this, [name, age, city]);
this.grade = grade;
this.school = school;
}
const s = new Student('小明', 18, '北京', '大三', '北大');
console.log(s.name, s.school); // '小明' '北大'
必须配合原型链才能继承方法
仅靠 call/apply 无法让子实例访问父构造函数原型上的方法(如 Animal.prototype.eat)。你需要额外设置原型关系:
- 推荐方式:
Object.setPrototypeOf(Dog.prototype, Animal.prototype) - 或兼容旧环境:
Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; - 否则即使属性继承了,调用
dog.eat()会报undefined is not a function
现代写法更推荐 class + extends
ES6 的 class 语法底层仍依赖原型,但语义清晰、自动处理 constructor 和原型链:
-
super(...args)等价于Parent.call(this, ...args) -
extends自动建立原型链,无需手动设置 - 可读性高、不易出错,是当前标准实践
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() { console.log(this.name + ' 在吃'); }
}
class Dog extends Animal {
constructor(name, age, breed) {
super(name, age); // 等价于 Animal.call(this, name, age)
this.breed = breed;
}
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











