javascript入门:prototype和面向对象的实现

由于工作需要,需要大量使用javascript,于是对其进行了一下学习。

学习任何一个语言,最重要的是掌握其和其他语言不同的关键特性。对javascript来说,我总结就是prototype。就像metatable之于lua的地位,如果理解了prototype,再加上其他语言的使用经验,javascript将很容易掌握。

首先,明确一个概念,javascript中所有东西皆是对象,包括函数。这是后面讨论的基础。

关于prototype,相关的概念有两个:

1 一切函数对象,都有一个默认的属性:prototype。

2 一切对象,都有一个默认的属性:__proto__。

比如我写一个函数:

function Speak()
{
        alert("speak!");
}

这个函数将会有一个叫做prototype的属性。

prototype更多的只是一个普通的对象(只有在使用new关键字的时候,它才会显出语法层面的特殊性);而__proto__则有特殊的语法意义:

当访问一个对象的属性,而这个属性不存在的时候,将在这个对象的__proto__中寻找这个属性,如果再找不到,就继续找__proto__的__proto__,直到最顶层(Object)。设置时则在对象上增加这个属性。

这个功能,类似于lua中的metatable中的index,如果metatable的index指定了一个表,那么找不到字段时会从那个表里找。

 

明白了__proto__的意义之后,再说prototype,事实上prototype就是为了设置__proto__而存在的:当使用new关键字调用函数,将把该函数对象的prototype设置到新实例的__proto__上。

例如:

1 function Animal(hp)
2 {
3     this.hp = hp;
4 }
5 
6 var a = new Animal(20);

此时,a的__proto__将是Animal的prototype。

prototype预置了一个属性叫constructor,constructor如果不主动修改,默认指向自己(本函数对象)。

因此,如果需要添加类成员,只需要在prototype上面加,prototype作为原型,只有一份,所有new出来的对象都使用它的属性。

1 function Animal(hp)
2 {
3     this.hp = hp;
4 }
5 
6 Animal.prototype.getHP = function() { return this.hp; }
7 
8 var a = new Animal(20);

此时,a.getHP()将调用到Animal.prototype.getHP。

关于类与实例,到这里就没问题了。可以认为prototype就是类模板,Animal(hp)就是构造函数。当调用new的时候,javascript做了下面的事情:
1 创建一个新的对象(Object)。

2 将该对象的__proto__赋值为new后所调用函数的prototype。

3 将该对象赋值给this参数(隐含在参数表中),调用该函数。

4 函数返回后,将该对象作为new表达式的返回值返回。

 

由于所有对象共享一个prototype,因此,此时要注意一点:如果要定义的类中有属性引用到可能被修改的对象,如:

1 function Animal(hp)
2 {
3 }
4 
5 Animal.prototype.family = {father:"a", mother:"b" };
6 
7 var a = new Animal();
8 var b = new Animal();

此时,如果修改了a的father,b的也一起被修改了。因此,这个family字段必须在Animal构造函数中设置,不能放在prototype中。如果该对象不太可能被修改,如纯粹的数值或字符串,就可以放到prototype中。

 

如果要实现继承,就要利用__proto__的链条形式往上查找属性的特性。将prototype指定为父类的实例。

 1 function Animal(hp)
 2 {
 3     this.hp = hp;
 4 }
 5 
 6 Animal.prototype.getHP = function() { return this.hp; }
 7 
 8 function Person(name)
 9 {
10     this.name = name;
11 }
12 
13 Person.prototype = new Animal();
14 Person.prototype.constructor = Person;
15 Person.prototype.getName = function() { return this.name; }
16 
17 var personInstance = new Person("Eagle");

此时,当我们访问personInstance.getHP,首先personInstance找不到,到personInstance.__proto__中寻找,即Person.prototype,因为Person.prototype是在Animal实例的基础上修改的,其__proto__是Animal.prototype,因此找到了Animal.prototype中的getHP。

 

解决了继承的问题,但是依然存在一个问题:如何调用父类的构造函数。上面说的getHP实际上调用会出错,因为Animal函数没有被调用过,因此,this.hp = hp这一句没有执行,其实是没有hp属性的。

此时,我们需要在构造函数中嵌套调用父类构造函数,如下:

 1 function Animal(hp)
 2 {
 3     this.hp = hp;
 4 }
 5 
 6 function Person(name, hp)
 7 {
 8     Animal.call(this, hp);    //注意这句话:函数对象的call方法可以显式的修改this指针。如果直接调用Animal(hp),this指向的是global。
 9     this.name = name;
10 }
11 
12 var a = new Person("Eagle", 100);

 

到此为止,通过prototype的一套体系,实现了javascript的类,实例和继承。

    原文作者:长空小鹰
    原文地址: https://www.cnblogs.com/eaglelun/p/5864961.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞