ECMAscript只支持实现继承,主要是依靠原型链来实现的。

JavaScript实现继承的方式:

  • 类式继承
  • 构造函数继承
  • 组合继承
  • 寄生组合式继承

1.类式继承

 //类式继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(){
this.name = 'dog';
} //类式继承父类(父类实例作为子类的原型对象)
Dog.prototype = new Animal('white'); //子类实例继承父类的属性和方法
var dog1 = new Dog();
console.log(dog1.name); //dog
console.log(dog1.type); //[ 'pig', 'cat' ]
dog1.greet('wangwangwang'); //'wangwangwang' //缺陷1:修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
dog1.type.push('dog');
var dog2 = new Dog();
console.log(dog2.type); //[ 'pig', 'cat', 'dog' ] //缺陷2:无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数
//无法为不同dog赋值不同的颜色,所有dog只能同一种颜色
console.log(dog1.color); //'white'
console.log(dog2.color); //'white'
  原理说明:在实例化一个类时,新创建的对象复制了父类的构造函数内的属性与方法并且将原型__proto__指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法。
 类式继承的两个缺陷
  (1)修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
  (2)无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数

2.构造函数继承

 //构造函数继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(color, age){
Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
this.age = age;
} //子类实例继承父类的属性和方法
var dog1 = new Dog('white', 1);
var dog2 = new Dog('red', 2);
dog1.type.push('dog'); console.log(dog1.color); //'white'
console.log(dog1.type); //[ 'pig', 'cat', 'dog' ]
console.log(dog2.color); //'red'
console.log(dog2.type); //[ 'pig', 'cat' ] //缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
dog1.greet('wangwangwang'); //TypeError: dog1.greet is not a function
dog2.greet('wangwangwang'); //TypeError: dog2.greet is not a function

  构造函数继承方式可以避免类式继承的缺陷,在子类构造函数内部调用父类构造函数。通过使用apply()和call()方法可以改变函数的作用域,在新创建的对象上执行构造函数。

所以在上面的例子中,我们在Dog子类中调用这个方法也就是将Dog子类的变量在父类中执行一遍,这样子类就拥有了父类中的共有属性和方法。

  构造函数继承也是有缺陷的,那就是我们无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

//缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
dog1.greet('wangwangwang'); //TypeError: dog1.greet is not a function
dog2.greet('wangwangwang'); //TypeError: dog2.greet is not a function

3.组合继承

组合继承其实就是将类式继承和构造函数继承组合在一起:

 //类式继承和构造函数继承结合的组合继承
//声明父类
function Animal(color) {
this.name = 'animal';
this.type = ['pig', 'cat'];
this.color = color;
}
// 为父类添加共有方法
Animal.prototype.greet = function(sound) {
console.log(sound);
} //声明子类
function Dog(color, age){
Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
this.age = age;
} //类式继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog; //子类实例继承父类的属性和方法
var dog1 = new Dog('white', 1);
var dog2 = new Dog('red', 2);
dog1.type.push('dog'); console.log(dog1.color); //'white'
console.log(dog1.type); //[ 'pig', 'cat', 'dog' ]
console.log(dog2.color); //'red'
console.log(dog2.type); //[ 'pig', 'cat' ] //获取到父类的共有方法
dog1.greet("wang"); //'wang'
dog2.greet("miao"); //'miao' //缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用
  在上面的例子中,我们在子类构造函数中执行父类构造函数,在子类原型上实例化父类,这就是组合继承了,可以看到它综合了类式继承和构造函数继承的优点,同时去除了缺陷。组合继承是JavaScript中最常用的继承方式。 
  可能你会奇怪为什么组合式继承可以去除类式继承中的引用缺陷?其实这是由于原型链来决定的,由于JavaScript引擎在访问对象的属性时,会先在对象本身中查找,如果没有找到,才会去原型链中查找,如果找到,则返回值,如果整个原型链中都没有找到这个属性,则返回undefined。也就是说,我们访问到的引用类型(比如上面的type)其实是通过apply复制到子类中的,所以不会发生共享。
组合继承的缺陷就是它调用了两次父类的构造函数。
//缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用

4.寄生组合式继承

寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数。由于现在用的最多的还是组合继承,寄生组合式继承不做深入的探究,有兴趣可以 参考js高程P172 。

总结

  JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问父类型的所有属性和方法,这一点与基于类的继承很相似。

  原型链的问题对象实例共享所有继承的方法和属性,修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例,因此不适宜单独使用。

  解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用父类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。但是使用这种方式导致的结果就是无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

  使用最多的集成模式是组合继承方式,这种模式综合了类式继承和构造函数继承的优点,同时去除了缺陷。即使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。

参考:JavaScript实现继承的方式

最新文章

  1. 学习OpenStack之 (1):安装devstack
  2. localStorage兼容ie6/7 用addBehavior 实现
  3. Apache Storm技术实战之1 -- WordCountTopology
  4. sublime text3的配置(整理)
  5. Bootstrap教程
  6. bootstrap轮播组件,大屏幕图片居中效果
  7. bzoj2180: 最小直径生成树
  8. ARM和x86的区别
  9. Android存储之SQLiteDatbase
  10. /dev/null 2>&1解释
  11. menu菜单项和menubutton菜单按钮的结合使用
  12. MySQL Hardware--CentOS 6查看CPU信息
  13. asp.net core Session的测试使用心得及注意事项
  14. Java并发编程:volatile关键字
  15. 关于栈和队列的一点点小知识-----C++自带函数
  16. 06-ICMP: Internet 控制报文协议
  17. UIButton vs UIEventListener 事件处理
  18. Linux学习之CentOS(四)----Linux各目录的介绍
  19. Js 运算符(加减乘除)
  20. springmvc 映射重复

热门文章

  1. Spring Boot 整合 ActiveMQ
  2. 你知道的和不知道的sass
  3. VS2010-MFC(对话框:为控件添加消息处理函数)
  4. jeecms v9 vue环境搭建
  5. 从三层架构到Spring框架
  6. day69test
  7. <每日一题>题目24:冒泡排序
  8. 11-1-break-continue
  9. Leetcode134. Gas Station加油站
  10. Property 'validate' does not exist on type 'Element | Element[] | Vue | Vue[]'. Property 'valid...