# One Day One Tip 之 **构造函数继承的几种实现方式**
>CreateTime:2016-08-31 14:11:49
JavaScript 实现继承的几种方式,有通过原型继承的, 有通过复制父类值继承的。
[TOC]
---
**有动物类【父类】和猫类【子类】**
~~~
function Animal(){
this.species = "动物";
}
function Cat(name,color){
this.name = name;
this.color = color;
}
~~~
### 方式一、构造函数绑定
>把子类的this对象传到Animal的方法里面,然后把父类的属性绑定到 子类的 this 上
**`Animal.apply(this,arguments)`**
~~~
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
~~~
### 二、prototype模式【常见】
>如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
~~~
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
~~~
`Cat.prototype = new Animal();`
把 Cat 类的 prototype 指向 父类的实例。
`Cat.prototype.constructor = Cat;`
是因为每个 prototype 都有一个 constructor 属性,它指向构造函数。 并且每一个类实例也有一个 constructor 属性, 默认指向 prototype.constructor 。
如果没有添加上这句代码的话, `cat1.constructor === Animal //true`
**虽然功能上使用没有出现什么问题,但是如果有使用到 cat.constructor 去做一些判断还是其他的操作, 会出现隐患。**
**cat1 明明是 从 Cat 类实例化出来的, 但是 cat.constructor却指向 Animal, 这个会造成继承紊乱。因此需要手动纠正,也就是这句代码的作用。**
### 三、直接继承prototype
>只继承 父类的 prototype 上的属性和方法
~~~
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
~~~
与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
所以,上面这一段代码其实是有问题的。请看第二行
~~~
Cat.prototype.constructor = Cat;
~~~
这一句实际上把Animal.prototype对象的constructor属性也改掉了!
~~~
alert(Animal.prototype.constructor); // Cat
~~~
### 四、利用空对象作为中介
由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。
~~~
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
~~~
F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。
~~~
alert(Animal.prototype.constructor); // Animal
~~~
**封装一个方法来实现继承**
~~~
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype; //备用性质,也可不加
}
~~~
使用如下
~~~
extend(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
~~~
### 五、 拷贝继承
上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。
首先,还是把Animal的所有不变属性,都放到它的prototype对象上。
~~~
function Animal(){}
Animal.prototype.species = "动物";
~~~
然后,再写一个函数,实现属性拷贝的目的。
~~~
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
~~~
这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。
使用的时候,这样写:
~~~
extend2(Cat, Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
~~~
## 转载文章
1. [Javascript面向对象编程(二):构造函数的继承](http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html)
- 前言
- 【00】如何写
- 【STAT法则写简历】
- 【01】前端
- 【20160829 前端面试题】
- 【腾讯IMWeb】笔试题(没有答案)
- 【桑世龙】前端笔试题(没有答案)
- 【浏览器输入URL后发生了什么】
- 【JS截图并生成图片】
- 【20160924】Sass 入门
- 【02】技巧
- 【01】GOOGLE搜索技巧
- 【02】Chrome跨域访问线上接口
- 【One Day One Tip】
- 【20160830】~ 闭包
- 【20160831】~ 继承的几种实现方式
- 【20160901】~浏览器输入URL到页面展示完成,发生了什么?(一)
- 【20160902】~浏览器输入URL,发生过程系列(转载)
- 【20160903】~ video在不同平台下的差异性
- 【20160906】~webpack之sourceMap
- 【20160909】ACE自定义代码提示
- 【20160910】Mac Nw.js 环境安装
- 【99】转载笔记
- 用一道面试题考察对闭包的理解