一个简单的原型链的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Person(name) {
this.name = name;
}

Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}.`);
};

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

// 设置Student.prototype的原型为Person.prototype
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function() {
console.log(`${this.name} is studying.`);
};

const student = new Student('Alice', 'A');
student.study();

设置子类原型对象实现继承的两种方式

1
2
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
1
Child.prototype = new Parent();

建议使用第一种,第二种容易产生bug

注意点和潜在的问题

(1)构造函数的属性和方法不应在原型上定义: 如果在构造函数内部定义属性和方法,而不是在原型上,那么这些属性和方法将不会被子类继承。只有在原型上定义的属性和方法才能被继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Parent() {
this.name = 'Parent';
this.sayHello = function() {
console.log(`Hello from ${this.name}`);
};
}
Parent.prototype.age = 30;

function Child() {}

Child.prototype = new Parent();

const child = new Child();
child.sayHello(); // 输出 "Hello from Parent"
console.log(child.age); // 输出 30

在上面的例子中,sayHello 是在构造函数内部定义的,而不是在原型上,所以它不会被子类 Child 继承。

(2)避免使用构造函数创建原型对象: 不建议使用 Child.prototype = new Parent(); 这种方式,因为它可能会引发一些问题,特别是当父类构造函数需要参数时。更好的做法是使用 Object.create 创建原型对象,如 Child.prototype = Object.create(Parent.prototype);

(3)修复子类的 constructor 属性: 在使用原型链继承时,通常会导致子类的 constructor 属性指向父类。为了修复这个问题,你应该手动将子类的 constructor 设置为正确的构造函数。

1
2
javascriptCopy code
Child.prototype.constructor = Child;

(4)避免在原型上修改引用类型的属性: 如果在原型上修改了一个引用类型(如数组或对象)的属性,这个修改会影响到所有继承自原型的对象,这可能会导致意外的行为。最好只在实例上修改引用类型的属性,以避免共享问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
javascriptCopy codefunction Parent() {
this.colors = ['red', 'green', 'blue'];
}

function Child() {}

Child.prototype = new Parent();

const child1 = new Child();
const child2 = new Child();

child1.colors.push('yellow');
console.log(child1.colors); // 输出 ['red', 'green', 'blue', 'yellow']
console.log(child2.colors); // 输出 ['red', 'green', 'blue', 'yellow']

(5)考虑使用其他继承模式: 原型链继承有其局限性,特别是在处理复杂继承关系和防止共享状态时。在某些情况下,可能更适合使用其他继承模式,如组合继承、寄生组合继承或类继承(ES6 中的 class 关键字)。