原文:理解JavaScript继承

对于JavaScript的继承和原型链,虽然之前自己看了书也听了session,但还是一直觉得云里雾里,不禁感叹JavaScript真是一门神奇的语言。这次经过Sponsor的一对一辅导和自己回来后反复思考,总算觉得把其中的精妙领悟一二了。

1. JavaScript创建对象

在面向对象语言中,通常通过定义类然后再进行实例化来创建多个具有相同属性和方法的对象。但是在JavaScript中并没有类的概念,不过ECMAScript中的构造函数可以用来创建特定类型的对象。因此,在JavaScript中可以创建自定义的构造函数,并且通过new操作符来创建对象。

在JavaScript中并没有“指定的”构造函数类型,构造函数实质上也是函数,它和一般函数的区别只在于调用方式不同。只有当通过new操作符来调用的时候它才可以作为构造函数来创建对象实例,并且把构造函数的作用域赋给这个新对象(将this指向这个新对象)。如果没有使用new来调用构造函数,那就是普通的函数调用,这个时候this指向的是window对象,这样做会导致所有的属性和方法被添加到全局,因此一定要注意命名构造函数时首字母大写,并且永远使用new来调用它。

function Person(name, gender) {
this.name = name;
this.gender = gender;
   this.say = function() {
    console.log("Hello");
  }
} var person1 = new Person("Mike", "male");
var person2 = new Person("Kate", "female");

这段代码就定义了一个构造函数Person, 并且给它添加了name和gender属性以及say方法。通过调用new操作符来创建了两个Person的实例person1和person2.可以通过代码来验证一下:

person1 instanceof Person; //true;
person2 instanceof Person; //true;

并且person1和person2都分别具有了name,gender属性,并且都被附上了构造对象时传入的值。同时它们也都具有say方法。

不过通过比较可以看出来,虽然这时person1和person2都具有say方法,但它们其实并不是同一个Function的实例,也就是说当使用new来创建构造函数的实例时,每个方法都在实例上重新被创建了一遍:

person1.say == person2.say; //false

这样的重复创建Function是没有必要的,甚至在实例变多的时候造成一种浪费。为此,我们可以使用构造函数的prototype属性来解决问题。prototype原型对象是用来寻访继承特征的地方,添加到prototype对象中的属性和方法都会被构造函数创建的实例继承,这时实例中的方法就都是指向原型对象中Function的引用了:

function Person(name, gender) {
this.name = name;
this.gender = gender;
} Person.prototype.say = function() {
console.log("Hello");
} var person1 = new Person("Mike", "male");
var person2 = new Person("Kate", "female"); person1.say == person2.say //true

2. prototype, constructor, 和__proto__

构造函数,原型对象,实例的关系是:JavaScript中,每个函数都有一个prototype属性,这是一个指针,指向了这个函数的原型对象。而这个原型对象有一个constructor属性,指向了该构造函数。每个通过该构造函数创建的对象都包含一个指向原型对象的内部指针__proto__。

用代码表示它们的关系:

Person.prototype.constructor === Person;
person1.__proto__ === Person.prototype;
person2.__proto__ === Person.prototype;

3. 继承的实现

JavaScript中使用原型链作为实现继承的主要方法。由于对象实例拥有一个指向原型对象的指针,而当这个原型对象又等于另一个类型的实例时,它也具有这个指向另一类型原型对象的指针。因此通过指向原型对象的指针__proto__就可以层层递进的找到原型对象,这就是原型链。通过原型链来完成继承:

function Teacher(title) {
this.title = title;
}
Teacher.prototype = new Person(); var teacher = new Teacher("professor");

这时,我们通过将Teacher的prototype原型对象指向Person的实例来完成了Teacher对Person的继承。可以看到Teacher的实例teacher具有了Person的属性和方法。

但是如果只是将构造函数的prototype原型对象指向另一对象实例,发生的事情其实可以归纳为:

Teacher.prototype instanceof Person //true
Teacher.prototype.constructor == Person //true
Teacher.prototype.__proto__ === Person.prototype //true

问题出现了:这时Teacher的构造函数变成了Person。虽然我们在使用创建的实例的属性和方法的时候constructor的类型并不会产生很大的影响,但是这依然是一个很不合理的地方。因此一般在使用原型链实现继承时,在将prototype指向另一个构造函数的实例之后需要再将当前构造函数的constructor改回来:

Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;

这样才是真正的实现了原型链继承并且不改变当前构造函数和原型对象的关系:

到这里,我们就可以将这个继承过程封装成一个extend方法来专门完成继承的工作了:

var extend = function(Child, Parent) {
Child.prototype = new Parent();
Child.prototype.constructor = Child;
return new Child();
};

现在这个方法接受两个参数:Child和Parent,并且在完成继承之后实例化一个Child对象并返回。我们当然可以根据需要来继续丰富这个函数,包括实例化的时候需要传入的参数什么的。

理解JavaScript继承的更多相关文章

  1. 理解JavaScript继承(三)

    理解JavaScript继承(三) 通过把父对象的属性,全部拷贝给子对象,也能实现继承. 7.浅拷贝 function extendCopy(p) { var o = {}; for (var pro ...

  2. 理解JavaScript继承(二)

    理解JavaScript继承(二) 5.寄生式继承 function object(o) { function F() {} F.prototype = o; return new F(); } fu ...

  3. 理解JavaScript继承(一)

    理解JavaScript继承(一) 我们都知道,面向对象的编程语言非常强大,之所以强大,就是其支持继承.在JavaScript中,也支持继承,而且有多种方法实现继承,比如原型链继承,借用构造函数继承, ...

  4. 彻底理解Javascript原型继承

    彻底理解Javascript原型继承 之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入. 本文试图进一步思考,争取彻底理解Javascript继承原理 实例成员 ...

  5. 理解Javascript的动态语言特性

    原文:理解Javascript的动态语言特性 理解Javascript的动态语言特性 Javascript是一种解释性语言,而并非编译性,它不能编译成二进制文件. 理解动态执行与闭包的概念 动态执行: ...

  6. 深入理解javascript原型和闭包(6)——继承

    为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...

  7. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  8. 深入理解JavaScript是如何实现继承的

    深入理解JavaScript是如何实现继承的-----------http://www.jb51.net/article/44384.htm

  9. 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链

    1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...

随机推荐

  1. hdu 4864 Task (馋)

    # include <stdio.h> # include <algorithm> # include <string.h> using namespace std ...

  2. Linux课程_系统配置和日常维护

    1.设置命令输入提示格公式:"username:当前文件夹$" 2.设置命令输入提示行格式为:"当前系统时间-用户#"(提示:Shell将通过反引號" ...

  3. Linux之父:除了写内核代码 别的真不会(转)

    Linus Torvalds 是 Linux 之父,被誉为活着的传奇. 其实很多人不了解他也并不奇怪,因为人是在是太低调了.现年 46 岁的他每天的工作仍然是编程,领导并推动着 Linux 的发展. ...

  4. Android应用开发:LoaderManager在Activity/Fragment中的使用分析

    LoaderManager 外部接口initLoader:起始 public <D> Loader<D> initLoader(int id, Bundle args, Loa ...

  5. CodeForces 396C 树状数组 + DFS

    本主题开始看到以为段树或树状数组,但是,对于一个节点的有疑问的所有子节点的加权,这一条件被视为树的根,像 然后1号是肯定在第一层中,然后建立一个单向侧倒查,然后记录下来 其中每个节点 层,终于 两个节 ...

  6. 职业选择測试(A/B卷)

    不同性格的人适合从事不同的职业.职业选择对于每一个人都是很重要的事情.假设能选一个既可以发挥潜能又有兴趣的工作,会使整个团队的效率逐倍增长.想了解你更适合什么职业吗?一起来測试一下吧.本套測试分为A卷 ...

  7. weighted Kernel k-means 加权核k均值算法理解及其实现(一)

    那就从k-means开始吧 对于机器学习的新手小白来说,k-means算法应该都会接触到吧.传统的k-means算法是一个硬聚类(因为要指定k这个参数啦)算法.这里利用百度的解释 它是数据点到原型的某 ...

  8. SQL Server系统数据库备份最佳实践

    原文:SQL Server系统数据库备份最佳实践 首先了解主要的系统数据库: 系统数据库 master 包含登录信息和其他数据库的核心信息 msdb 存储作业.操作员.警报.备份还原历史.数据库邮件信 ...

  9. Redis集群方案及实现

    在作出Redis群集解决方案,他跑了小半个.行表现得非常稳定在几乎相同的经历与大家分享,我写在前面的文章数据在线服务的一些探索经验,能够做为背景阅读 应用 我们的Redis集群主要承担了下面服务:1. ...

  10. css Tab选项卡1

    利用   锚点原理 以及overflow:hiden 结合,实现纯  css  tab 方式 兼容ie6 + 适合单个tab   不需要js          注意点  红色方框的   a 对应a   ...