js学习笔记-原型继承

js学习笔记,原型对象、原型链、继承。js常用的几种继承方式

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Parent () {
this.name = '张三';
this.friends=[ '李四', '王五'];
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.friends.push('赵六');
console.log(child1.getName()) // 张三
console.log(child1.friends) // [ '李四', '王五','赵六']
var child2 = new Child();
console.log(child2.friends); // [ '李四', '王五','赵六']

缺点:

  • 1.引用类型的属性被所有实例共享

  • 2.在创建 Child 的实例时,不能向Parent传参

借用构造函数 (类式继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Parent(name) {
this.name = name;
this.age = 10;
}
Parent.prototype.doSomthing = function() {
console.log('parent do something!');
};
function Child(name) {
// 通过call,apply 改变对象的this指向来继承
Parent.call(this, name);
// Parent.apply(this, arguments);
}
Child.prototype.doSomthing = function() {
console.log('child do something!');
};
var child = new Child('张三');
child.doSomthing(); // TypeError: child.doSomthing is not a function

优点:

  • 避免了引用类型的属性被所有实例共享

  • 可以在 Child 中向 Parent 传参

缺点:

  • 无法复用一些公用函数

  • 方法都在构造函数中定义,每次创建实例都会创建一遍方法

组合式继承(原型+构造函数继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Parent(name) {
this.name = name;
this.age = 10;
}
Parent.prototype.say = function() {
console.log('parent name is ' + this.name + '!');
};
Parent.prototype.doSomthing = function() {
console.log('parent do something!');
};
function Child(name) {
// 通过call,apply 改变对象的this指向来继承
Parent.call(this, name); // 第二次调用
// Parent.apply(this, arguments);
}
Child.prototype = new Parent('father'); // 第一次调用
Child.prototype.constructor = Child;
Child.prototype.say = function() {
console.log('child name is ' + this.name + '!');
};
var child = new Child('张三');
child.say(); // child name: 张三
child.doSomthing(); // parent do something!

优点:

  • 融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式

缺点:

  • 组合继承使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。

原型式继承

在没有必要兴师动众创建构造函数,而只想让一个对象与另一个对象保持类似的情况下使用这种继承方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createObj(o) {
function F(){}
F.prototype = o;
return new F();
}
var obj = { name: '张三', friends: ['李四', '王五'] };
var obj1 = object(obj);
obj1.name = '刘三好';
obj1.friends.push('赵六');
console.log(obj1.name); // 刘三好
console.log(obj1.friends); // ["李四", "王五", "赵六"]
var obj2 = object(obj);
console.log(obj2.name); // 张三
console.log(obj2.friends); // ["李四", "王五", "赵六"]

缺点:

  • 引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

1
2
3
4
5
6
7
function createObj (o) {
var clone = object.create(o); // 创建对象
clone.sayName = function () { // 增强对象
console.log('hi');
}
return clone; // 返回指定对象
}

缺点:

  • 跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

寄生组合式继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
var child1 = new Child('张三', '20');
console.log(child1.name); // 张三

优点:

  • 跟组合式继承的区别在于,不需要在一次实例中调用两次父类的构造函数

相关参考

显示 Gitment 评论