发表于: 2017-05-03 21:58:51
1 1055
1.背景介绍
JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。
2.知识剖析
继承概念:
指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力, 继承是类与类或者接口与接口之间最常见的关系.
继承方式:
在基于类的面向对象方式中,对象(object)依靠类(class)来产生。
而在基于原型的面向对象方式中,对象(object)则是依靠构造器(constructor)利用原型(prototype)构造出来的。
JavaScript语言正是如此,它是通过一种叫做原型(prototype)的方式来实现面向对象编程。
继承分类及使用场景:
单继承(实例类继承) 场景:扩充/改善基类
多继承(纯抽象类继承) 场景:实现多态
3.常见问题
js如何实现继承?
4.解决方案
既然要实现继承,那么首先我们得有一个父类,代码如下:
// 定义一个动物类
function Animal (name) {
// 属性
this.name = 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
方法一:原型链继承
核心: 将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特点:
非常纯粹的继承关系,实例是子类的实例,也是父类的实例
父类新增原型方法/原型属性,子类都能访问到
缺点:
要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
无法实现多继承
来自原型对象的引用属性是所有实例共享的,在编码实战中详细讲解.
创建子类实例时,无法向父类构造函数传参
方法二:构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){
Animal.call(this);
this.name = name;
}
// Test Code
var cat = new Cat("Tom");
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
解决了1中,子类实例共享父类引用属性的问题 创建子类实例时,实例可以向父类传递参数 可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例。
只能继承父类的实例属性和方法,不能继承原型属性/方法。
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。
方法三:组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){
Animal.call(this);
this.name = 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
特点:
弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法 既是子类的实例,也是父类的实例 不存在引用属性共享问题 可传参 函数可复用
缺点:
调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
5.编码实战
关键点:属性查找过程
执行tom.features.push,首先找tom对象的实例属性(找不到), 那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的 features属性中插入值。 在console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。 刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。
6.扩展思考
继承、依赖、接口的区别?
7.参考文献
参考:JS继承的实现方式
参考:总结继承的几种方式
参考:UML类图详解接口、继承、依赖
评论