JavaScript实现继承的方式和各自的优缺点
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__
指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法。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引擎在访问对象的属性时,会先在对象本身中查找,如果没有找到,才会去原型链中查找,如果找到,则返回值,如果整个原型链中都没有找到这个属性,则返回undefined。也就是说,我们访问到的引用类型(比如上面的type)其实是通过apply
复制到子类中的,所以不会发生共享。组合继承的缺陷就是它调用了两次父类的构造函数。
//缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用
4.寄生组合式继承
寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数。由于现在用的最多的还是组合继承,寄生组合式继承不做深入的探究,有兴趣可以 参考js高程P172 。
总结
JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问父类型的所有属性和方法,这一点与基于类的继承很相似。
原型链的问题对象实例共享所有继承的方法和属性,修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例,因此不适宜单独使用。
解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用父类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。但是使用这种方式导致的结果就是无法获取到父类的共有方法,也就是通过原型prototype
绑定的方法。
使用最多的集成模式是组合继承方式,这种模式综合了类式继承和构造函数继承的优点,同时去除了缺陷。即使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。
JavaScript实现继承的方式和各自的优缺点的更多相关文章
- javascript 中继承实现方式归纳
转载自:http://sentsin.com/web/1109.html 不同于基于类的编程语言,如 C++ 和 Java,javascript 中的继承方式是基于原型的.同时由于 javascrip ...
- javascript实现继承的方式
this this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用a ...
- JavaScript的继承实现方式
1.使用call或apply方法,将父对象的构造函数绑定在子对象上 function A(){ this.name = 'json'; } function B(){ A.call(this); } ...
- JavaScript 的继承与多态
本文先对es6发布之前javascript各种继承实现方式进行深入的分析比较,然后再介绍es6中对类继承的支持以及优缺点讨论.最后介绍了javascript面向对象编程中很少被涉及的“多态”,并提供了 ...
- javascript实现继承的几种方式
原型链方式实现继承 function SuperType(){ this.property = true; this.colors = ['red','blue','green']; } SuperT ...
- JavaScript各种继承方式和优缺点
好久没写博客啦,嘻嘻,这个月是2017年的最后一个月啦,大家应该都开始忙着写年终总结了吧,嘻嘻,小颖今天给大家分享下Javascript中的几种继承方式以及他们的优缺点. 1.借助构造函数实现继承 原 ...
- DOM笔记(十三):JavaScript的继承方式
在Java.C++.C#等OO语言中,都支持两种继承方式:接口继承和实现继承.接口继承制继承方法签名,实现继承则继承实际的方法和属性.在SCMAScript中,由于函数没有签名,所以无法实现接口继承, ...
- 【编程题与分析题】Javascript 之继承的多种实现方式和优缺点总结
[!NOTE] 能熟练掌握每种继承方式的手写实现,并知道该继承实现方式的优缺点. 原型链继承 function Parent() { this.name = 'zhangsan'; this.chil ...
- javascript继承(七)—用继承的方式实现照片墙功能
照片墙DEMO下载 注意:图片有四种类型:1可放大:2可拖动:3既可放大也可拖动:4都不行.由于每个图片的构造函数不同而不同(目前在火狐上调试的,其它的浏览器可能不行,请见谅,主要讲继承的思想.以后会 ...
随机推荐
- LINUX挂接Windows文件共享
Windows网络共享的核心是SMB/CIFS,在linux下要挂接(mount)windows的磁盘共享,就必须安装和使用samba 软件包.现在流行的linux发行版绝大多数已经包含了samba软 ...
- C++ 系列:C++ 内存布局
1 前言 了解你所使用的编程语言究竟是如何实现的,对于C++程序员可能特别有意义.首先,它可以去除我们对于所使用语言的神秘感,使我们不至于对于编译器干的活感到完全不可思议:尤其重要的是,它使我们在De ...
- 判断JS对象是否为空的几种方式
.将json对象转化为json字符串,再判断该字符串是否为"{}" var data = {}; var b = (JSON.stringify(data) == "{} ...
- Java系列笔记(4) - JVM监控与调优【转】
Java系列笔记(4) - JVM监控与调优[转] 目录 参数设置收集器搭配启动内存分配监控工具和方法调优方法调优实例 光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在 ...
- php计算网段内所有IP,分配子网段
由于最近业务需要,写了个获取网段内所有IP的函数,以及分配可用子网段的函数 /** * 根据网段获取计算所有IP * @param string $segment 网段 '139.217.0.1/24 ...
- Java英语面试题(核心知识篇)
Java英语面试题(核心知识篇) Question: What is transient variable?Answer: Transient variable can't be serialize. ...
- iOS之UIButton的normal和selected状态切换
1.问题? 改变播放器按钮状态,发现从selected切回到normal状态时候,中间会切换normal-selected-normal的图片? 2.原因…… normal-->selected ...
- sed应用 升级场景配置文件更新 指定行追加
function addLine() { confFile=configuration.xml isExist=`cat ${confFile} | grep "<listen_ena ...
- GC 案例收集整理
1.数组动态扩容 现象:系统一直在做cms gc,但是老生代一直不降下去,但是执行一次jmap -histo:live之后,也就是主动触发一次full gc之后,通过jstat -gcutil来看老 ...
- HDU--2126 Buy the souvenirs(二维01背包)
题目http://acm.hdu.edu.cn/showproblem.php?pid=2126 分析:有两个要求,一是计算最多可以选多少中纪念品:而是计算选最多纪念品的方案有多少种, 即统计最优方案 ...