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实现继承的方式

JavaScript实现继承的方式和各自的优缺点的更多相关文章

  1. javascript 中继承实现方式归纳

    转载自:http://sentsin.com/web/1109.html 不同于基于类的编程语言,如 C++ 和 Java,javascript 中的继承方式是基于原型的.同时由于 javascrip ...

  2. javascript实现继承的方式

    this this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用a ...

  3. JavaScript的继承实现方式

    1.使用call或apply方法,将父对象的构造函数绑定在子对象上 function A(){ this.name = 'json'; } function B(){ A.call(this); } ...

  4. JavaScript 的继承与多态

    本文先对es6发布之前javascript各种继承实现方式进行深入的分析比较,然后再介绍es6中对类继承的支持以及优缺点讨论.最后介绍了javascript面向对象编程中很少被涉及的“多态”,并提供了 ...

  5. javascript实现继承的几种方式

    原型链方式实现继承 function SuperType(){ this.property = true; this.colors = ['red','blue','green']; } SuperT ...

  6. JavaScript各种继承方式和优缺点

    好久没写博客啦,嘻嘻,这个月是2017年的最后一个月啦,大家应该都开始忙着写年终总结了吧,嘻嘻,小颖今天给大家分享下Javascript中的几种继承方式以及他们的优缺点. 1.借助构造函数实现继承 原 ...

  7. DOM笔记(十三):JavaScript的继承方式

    在Java.C++.C#等OO语言中,都支持两种继承方式:接口继承和实现继承.接口继承制继承方法签名,实现继承则继承实际的方法和属性.在SCMAScript中,由于函数没有签名,所以无法实现接口继承, ...

  8. 【编程题与分析题】Javascript 之继承的多种实现方式和优缺点总结

    [!NOTE] 能熟练掌握每种继承方式的手写实现,并知道该继承实现方式的优缺点. 原型链继承 function Parent() { this.name = 'zhangsan'; this.chil ...

  9. javascript继承(七)—用继承的方式实现照片墙功能

    照片墙DEMO下载 注意:图片有四种类型:1可放大:2可拖动:3既可放大也可拖动:4都不行.由于每个图片的构造函数不同而不同(目前在火狐上调试的,其它的浏览器可能不行,请见谅,主要讲继承的思想.以后会 ...

随机推荐

  1. Luogu P2079 烛光晚餐(背包)

    P2079 烛光晚餐 题意 题目背景 小明准备请小红去一家咖啡厅,共进烛光晚餐.小红高兴地和他一起去了咖啡厅. 题目描述 小红说:"小明,你点菜吧."小明看到菜单上有\(N\)道菜 ...

  2. loj2212 方伯伯的OJ

    题意: n<=1e8,m<=1e5. 标程: #include<cstdio> #include<algorithm> #include<cstring> ...

  3. C++ 类设计核查表

    参考:https://www.jianshu.com/p/01601515ca31 <大规模C++程序设计> 函数接口: 1.运算符或非运算符函数? 2.自由或成员运算符? 3.虚函数或非 ...

  4. about how to determine a prime number

    (1) if divided by 2 or 3, then no; (2) we only have to go through prime factors; because a composite ...

  5. string、char* 、int数据类型相互转换

    string类型转换成char*类型,这里一般有以下三种方法: 1.c_str()方法 string name="Qian"; char *str=(char*)name.c_st ...

  6. php中$_REQUEST、 $_GET、 $_POST、 $_COOKIE 的关系和区别

    看到REQUEST可以通吃GET .POST .COOKIE 后 感觉这个$_REQUEST太强大了是不是其他的几个超级变量就没有用了,下面对他们整体做个比较: 1.安全性 post>get 2 ...

  7. EF实体模型的更新

    摘要 解决前期数据库优先添加的实体,然后数据库表结构发生变化后,导致代码操作EF插入更新数据失败问题 EF 数据库更新模型 相比大家在使用实体操作数据库的时候,都是采取数据库优先,手动添加实体模型.但 ...

  8. Android之FrameLayout帧布局

    1.简介 这个布局直接在屏幕上开辟出一块空白的区域,当我们往里面添加控件的时候,会默认把他们放到这块区域的左上角; 帧布局的大小由控件中最大的子控件决定,如果控件的大小一样大的话,那么同一时刻就只能看 ...

  9. java浮点运算的陷阱

    首先呢,i你要明白double和float. Inifinity: 例如:syso(5.0/0.0+" 和"+0.0 /0.0); 浮点运算的0.0不是真正意义上的0,而是非常接近 ...

  10. AlexNet详细解读

    AlexNet详细解读 目前在自学计算机视觉与深度学习方向的论文,今天给大家带来的是很经典的一篇文章 :<ImageNet Classification with Deep Convolutio ...