(二)个人JavaScript系列:JavaScript面向对象旅程(下)

剪不断,理还乱,是离愁。javascript

前面已经提到过新语言开发的两个步骤,分别是:1、定义基本的数据类型,完善结构化编程语言的设计;2、为函数类型绑定this的概念,好在对象的方法中能够引用到对象自身。下面是继续下去的思路,其主体思想是尽量地引用传统面向对象语言的相关概念(如类、继承等)到新语言中来。java

3、让对象属于某个类

此次要引入类的概念来。可是注意的是,仍是前面提到过的思路,是让对象看起来属于某个类,而不是真正地构造基于类的种种语义概念。编程

通常来讲,类包括类符号和类模板。最简单的类符号能够是一个字符串属性。好比随便一个对象,它的class属性来指明类名,即调用obj.class返回类名字符串(如"Cat", "Dog"等)。不过麻烦的是类模板。咱们要在统一的地方定义类模板,以便于在用统一的模式建立类实例。这样建立的类实例(即对象)才是真正有意义的。由于咱们不只要在类符号上来区别类;更重要的是,要在类行为上来统一类。编程语言

这时想到的是函数。由于在JavaScript中,一切对象的建立经过一段代码块来实现的,而函数又可以将这段代码块组合起来。因此,可让通常的函数做为类模板的定义;进一步地,将它视为构造方法。函数

通常的函数定义是:ui

function createStudent(name, age) {
    var student = {};
    student.name = name;
    student.age = age;
    student.toString = function() {
        return this.name + " " + this.age;
    };
    student.class = 'Student';
    return student;
}

这样才可以完整地表现咱们以前的一些概念。并且,这种方式没有引入任何新的语言概念。只不过,这种构造方式要彻底依赖开发人员去实现。语言自己并不能自动支持其中的任何一个概念。this

所以,当时的JavaScript设计者进一步地推进了在语言自身中去自动实现一些概念。分为如下几步:prototype

1. 引入构造函数的概念

上面的createStudent多多少少不是构造函数的样子。像Java和C++那样的面向对象语言,当调用构造函数时,对象已经建立好了。构造函数完成的是一些初始化的工做。根本就不须要像第2行代码那样去显示地建立对象。因此,前面的代码要改写成下面的样子:设计

function Student(name, age) {
    this.name = name;
    this.age = age;
    this.toString = function() {
        return this.name + " " + this.age;
    };
}

这里主要作了如下几点改动:code

1. 函数名再也不是createStudent,而是Student,这看起来更像是构造函数的名字。
2. 再也不显示地建立对象;而是当函数做为构造函数调用时,会默认构造一个空对象,并可以经过this访问。
3. 函数再也不return返回任何对象;而是默认地,应该返回this指向地对象。
4. 再也不有任何显示地构造this.class = "Student"之类的语句;而是默认地,这一构建应该在构造过程当中自动完成。

只不过,这里说是这样说,要达到这一系列的改动必需要作一些工做。若是还像之前那样调用Student函数时达不到上面提到的四点效果的(说错了,其实第一点效果达到了)。有兴趣的同窗能够本身揣摩下。

为达到上面提到的四点效果,JavaScript设计者引入一个新的new语句。它像Java构造对象时那样调用。像下面:

new Student("Sam", 18);

而上面语句实际作的工做,用JavaScript描述大体就是:

var obj = {};
Student.call(obj, "Sam", 18);
obj.i_was_build_by = Student;
return obj;

这里特别注意的是代码的第三行。咱们再也不是经过增长一个class属性来区分对象的类,而是经过加入一个i_was_build_by属性,它引用了构造函数Student。这个至关于前面的class属性,不过它引用的不是一个单纯的字符串,而是一个函数了。这样也行。咱们也能够相似的判断一个对象是否属于某个类:

s instanceof Student //等效于 s.i_was_build_by == Student

我写出这么一个奇葩的名字,是不想误导读者。若是要深追究,JavaScript当中不是经过这种方式来区别对象的,其机制要稍微复杂些,不过大致思想是一致的。

让方法只定义一次

咱们看到改动后的代码,其依然有个不足之处。在基于类模板的语言中,方法是属于类的,只须要定义一次。而在咱们的版本中,方法是属于对象的,其在每次Student函数调用过程中都会被定义一次。且不说带来的内存消耗吧。这样离看上去像Java也是差了些。因此这里又要作些改动,使得方法只须要定义一次。

思路就是新建立的对象要保持一个对象引用,这个对象囊括了对象所属类的方法集合。首先,这个引用的名字是prototype;其次,它的来源是构造函数同为名prototype的引用;最后,全部在本对象中找不到的方法,都推到prototype中去查找。例如,咱们要把以前的案例像下面这样写:

function Student(name, age) {
    this.name = name;
    this.age = age;
}

Student.prototype.toString = function() {
    return this.name + " " + this.age;
};

var s = new Student();
//s.prototype == Student.prototype;
//s.toString() == s.prototype.toString.call(s);

解释:Student函数自己有个属性prototype。经过new Student()语句构造的s对象,它的prototype属性指向了函数Student的prototype属性。最后当调用s.toString()时,因为s中不存在toString属性,继而跳到prototype对象中去查找。就好像prototype当中的属性是本身的属性同样。

那么真正地new Student("Sam", 18)语句执行逻辑能够总结以下:

var obj = {};
Student.call(obj, "Sam", 18);
obj.i_was_build_by = Student;
obj.prototype = Student.prototype;
return obj;

经过这种方式,咱们能够只须要在prototype处定义方法一次便可;另外,prototype也能够定义类的共有属性。这就是prototype处的做用。下面咱们还会看到,经过prototype链的方式,它也开拓了通往继承之门的道路。

4、继承并不神秘,它就是prototype链

真的快要写完了。也许在JavaScript中,最值得着墨的地方就是继承了。不过我写的有些累了,这里再也不多提了。

其思路就是扩展prototype下去。咱们以前提过,若是一个对象的属性找不到,就会在它的prototype引用中去找;若是在prototype引用中还找不到呢?那么就会在prototype引用的prototype引用中再去找,一直到找到为止或者prototype引用为空。

但这与继承有什么联系呢?事实上,经过巧妙地构造prototype链,就能够实现继承的效果了。不便说了,上例子吧:

function Animal() {}

function Dog() {}

Dog.prototype = new Animal();

这便实现了继承的魔法。乍一看也许没明白,须要拆解开:

let animal = new Animal();
animal.prototype == Animal.prototype;

let Dog.prototype = animal;

let dog = new Dog();
dog.prototype == Dog.prototype == animal;

上面的例子显示了,若是新建一个dog对象,它的prototype对象(暂且取个中间变量)为animal,而animal对象的prototype对象就会回到Animal的prototype中去。对于dog的某个方法调用,它首先在animal中寻找(这个是Dog的prototype);若是找不到,就会在animal的prototype中寻找(这个是Animal的prototype)。这样,咱们不只能够调用Dog.prototype中定义的方法(这是子类的方法),也能够调用Animal.prototype的方法(这个是继承于父类的方法)。这样操做便实现了继承。

5、总结

JavaScript按照这种思路就创造得差很少了。这种思路差很少就是JavaScript面向对象的一种概述了。而实际上,JavaScript真正地内部机制比起这个要复杂一些;不过我相信它也有本身的考量。总之,JavaScript有着本身的面向对象思想,又要引入传统的基于类模板的面向对象概念进来,就变成了如今这样了。

相关文章
相关标签/搜索