前言

  一个多月前,卤煮读了一篇翻译过来的外国人写的技术博客。此君在博客中将js中的类(构造)继承和原型继承做了一些比较,并且得出了结论:建议诸位在开发是用原型继承。文中提到了各种原型继承的优点,详细的露珠不一一说明介绍了。这篇文章的名字是为什么原型继承很重要,有兴趣的同学可以去看,此文有些深度,值得反复阅读。今天这篇文章也来谈谈js中的继承方式以及它们的优缺点。

类式继承(构造函数)

  JS中其实是没有类的概念的,所谓的类也是模拟出来的。特别是当我们是用new 关键字的时候,就使得“类”的概念就越像其他语言中的类了。类式继承是在函数对象内调用父类的构造函数,使得自身获得父类的方法和属性。call和apply方法为类式继承提供了支持。通过改变this的作用环境,使得子类本身具有父类的各种属性。

var father = function() {
this.age = 52;
this.say = function() {
alert('hello i am '+ this.name ' and i am '+this.age + 'years old');
}
} var child = function() {
this.name = 'bill';
father.call(this);
} var man = new child();
man.say();

原型继承

  原型继承在开发中经常用到。它有别于类继承是因为继承不在对象本身,而在对象的原型上(prototype)。每一个对象都有原型,在浏览器中它体现在一个隐藏的__proto__属性上。在一些现代浏览器中你可以更改它们。比如在zepto中,就是通过添加zepto的fn对象到一个空的数组的__proto__属性上去,从而使得该数组成为一个zepto对象并且拥有所有的方法。话说回来,当一个对象需要调用某个方法时,它回去最近的原型上查找该方法,如果没有找到,它会再次往下继续查找。这样逐级查找,一直找到了要找的方法。 这些查找的原型构成了该对象的原型链条。原型最后指向的是null。我们说的原型继承,就是将父对像的方法给子类的原型。子类的构造函数中不拥有这些方法和属性。

var father = function() {
}
father.prototype.a = function() {
}
var child = function(){}
//开始继承
child.prototype = new father();
var man = new child();
man.a();

可以看到第七行实现了原型继承。很多人并不陌生这种方式。通过在浏览器中打印man我们就可以查看各个原型的继承关系。

可以看到逐级的关系child->object(father实例化的对象)->father。child是通过中间层继承了father的原型上的东西的。但是为什么中间还有一层object呢,为什么不把child.prototype = father.prototype。答案是如果这样做child和father就没有区别了。大家应该还记得在prototype中有个constructor属性,指向的是构造函数。按照正常的情况我们要把constructor的值改回来指向child的构造函数。但如果直接把father.prototype赋值给child.prototype,那么constructor应该指向谁呢?所以很显然只能通过中间层才能使得child和father保持为独立的对象。

对比

  和原型对比起来,构造函数(类)式继承有什么不一样呢?首先,构造函数继承的方法都会存在父对象之中,每一次实例,都回将funciton保存在内存中,这样的做法毫无以为会带来性能上的问题。其次类式继承是不可变的。在运行时,无法修改或者添加新的方法,这种方式是一种固步自封的死方法。而原型继承是可以通过改变原型链接而对子类进行修改的。另外就是类式继承不支持多重继承,而对于原型继承来说,你只需要写好extend对对象进行扩展即可。

组合模式

  另外的一种模式,是结合类继承和原型继承的各自优点来进行对父类的继承。用类式继承属性,而原型继承方法。这种模式避免了属性的公用,因为一般来说,每一个子类的属性都是私有的,而方法得到了统一。这种模式称为组合模式,也是继承类式常用到的一种方法。

function father() {
this.a = 'father'
} father.prototype.b = function() {
alert(this.a)
} var child = function() {
father.call(this)
} child.prototype = new father();

new 关键字和Obeject.create方法

在文章中,博主指出了使用new关键字的弊端。他说:“new关键字掩盖了Javascript中真正的原型继承,使得它更像是基于类的继承。其实new关键字只是Javascript在为了获得流行度而加入与Java类似的语法时期留下来的一个残留物”。作者推荐我们使用Object.create方法创建或者实例化对象。露珠做过测试,使用new和使用object.create方法都是将对象添加到原型上去。我们可以看一下代码:

var father = function() {
this.a = 'father'
}
father.prototype.b = function() {alert(this.a)}
var obj = new father();

  在浏览器中打印obj,可以观察它的结构。它本身是一个对象,有自身属性,同时在其__proto__熟悉上也有b方法。在__proto__的后面有father,可以看出原型是自father来的。

那么objcet.create方法呢,我们也可以通过下面代码测试之:

var father = {
a: 'father',
b: function() {
alert(this.a);
}
} var obj = Object.create(father);
console.dir(obj)

下面是浏览器输出的结果:

可以看到,用create的方法构造出来的对象,a属性和b方法都是在对象的原型上,也就是说我们可以通过更改father的属性动态改变obj的原型上的方法和属性,而上面通过new关键字用构造函数生成的实例,a属性是无法改变的。从这里,我们也可以看到类继承和原型基础的一些区别。

结论

  原型继承比较符合js这种语言的特点。因为它本身就是js强大的原型的一部分。而类式继承,与其称它为继承方式,毋宁说是一种函数的运用技巧来模拟继承罢了。本文是卤煮的一己之见,错误偏颇在所难免,如果有之,请各位斧正。

JS原型继承和类式继承的更多相关文章

  1. js原生设计模式——2面向对象编程之继承—new类式继承

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  2. js原生继承之——类式继承实例(推荐使用)

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  3. JavaScript中的类式继承和原型式继承

    最近在看<JavaScript设计模式>这本书,虽然内容比较晦涩,但是细品才发现此书内容的强大.刚看完第四章--继承,来做下笔记. 书中介绍了三种继承方式,类式继承.原型式继承和掺元类继承 ...

  4. JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)

    继承的其他形式: •类式继承:利用构造函数(类)继承的方式 •原型继承:借助原型来实现对象继承对象   类 : JS是没有类的概念的 , 把JS中的构造函数看做的类 要做属性和方法继承的时候,要分开继 ...

  5. js原生设计模式——2面向对象编程之继承—原型继承(类式继承的封装)

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  6. JavaScript 类式继承与原型继承

    交叉着写Java和Javascript都有2年多了,今天来总结下自己所了解的Javascript类与继承. Javascript本身没有类似Java的面向对象的类与继承术语,但其基于原型对象的思想却可 ...

  7. 精读JavaScript模式(八),JS类式继承

    一.前言 这篇开始主要介绍代码复用模式(原书中的第六章),任何一位有理想的开发者都不愿意将同样的逻辑代码重写多次,复用也是提升自己开发能力中重要的一环,所以本篇也将从“继承”开始,聊聊开发中的各种代码 ...

  8. JS原型继承与类的继承

    我们先看JS类的继承 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

  9. js类式继承模式学习心得

    最近在学习<JavaScript模式>,感觉里面的5种继承模式写的很好,值得和大家分享. 类式继承模式#1--原型继承 方法 让子函数的原型来继承父函数实例出来的对象 <script ...

随机推荐

  1. html视频播放器的代码 及其参数详解

    播放视频最实用的一段代码是: 程序代码 <"></embed></object> 其他的看参数自己修改吧 .avi格式 代码片断如下: 程序代码 < ...

  2. linux内核(kernel)版本号的意义

    转自:http://www.cnblogs.com/jsjliuxing/archive/2011/12/01/2271182.html 在linux下有一个目录,即/usr/src/kernels/ ...

  3. JavaOO面向对象中的注意点(三)

    1.接口 a.关键字:interface public interface Serviceable{ //TODO } b.属性:只能是 公共 静态 常量 属性--就算不写这三个关键字,也是默认这种情 ...

  4. linux中redis的php扩展安装

    PHP中的扩展一般都是在安装环境的时候就已经装好了的.但是有的一些扩展在后期想要加上去的话也是可以的.php支持后期安装扩展. 想要安装扩展就需要先去下载安装扩展所需要的扩展源码包.autoconf. ...

  5. Tween Animation----Alpha渐变透明度动画

    本博文是我自己操作过的并且能运行才给大家分享的 layout ----activity_main.xml 在res/新建一个anim文件夹,用来保存动画属性的xml 在anim文件夹里新建一个alph ...

  6. Jsonp跨域

    Jsonp.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  7. win下安装jdk7后,修改环境变量无法改为jdk6的问题

    jdk的安装包,现在的策略改变了,不再帮你设置环境变量,而是直接把java.exe,javaw.exe,javaws.exe直接拷贝到了system32下. 删掉这3个,path就生效了!

  8. 全局变量 HInstance 到底是在什么时候赋值的?

    在学习 资源文件 和 钩子函数 时, 经常用到当前模块句柄(HInstance)这个全局变量. 今天特别想知道, 它到底是在什么时候给赋值的. 输入 HInstance; "Ctrl+鼠标& ...

  9. iOS 键盘遮挡输入 解决办法

    .初始化及添加通知观察者 - (void)viewDidLoad { [super viewDidLoad]; self.tableView = [[UITableView alloc] initWi ...

  10. solr4.5安装配置 linux+tomcat6.0+mmseg4j-1.9.1分词

    首先先介绍下solr的安装配置 solr下载地址 (我这用的solr-4.5.0) 运行环境 JDK 1.5或更高版本 下载地址(Solr 4以上版本,要求JDK 1.6)  我用的JDK1.6 ) ...