今天是第一次在《博客园》写东西,总感觉自己掌握的东西都太少了写上来恐让大家笑话!就当做笔记记录下相关的内容共享吧!
JavaScript是一种弱类语言没有像那些强类型的语言那样有一个独一无二的继承方法来完成相关函数之间的继承关系,在JavaScript中如果想达到继承的效果就要采取一系列的措施。JavaScript属于使用原型式继承的少数语言之一。
不多废话,进入主题。JavaScript继承的方法有很多种,分别是:“类式继承”,“原型式继承”。我目前就知道这些,可能还有其他方法继承就请大家补充一下了。
一、类式继承
下面让我们看一下例子:
function Box(name){ this.name = name; } Box.prototype.getName = function(){ return this.name; }
我们首先要创建一个构造函数,类名按惯例首字母应该大写。要想创建该类的实例,只需要用关键字new来调用这个构造函数即可:
var smallBox = new Box('Small Box'); smallBox.getName();
这样我们就可以得到Box的name名字了。现在要继承Box的类可能要稍微复杂些:
function BigBox(name, color){ Box.call(this, name); this.color = color; } BigBox.prototype = new Box(); BigBox.prototype.constructor = BigBox; BigBox.prototype.getColor = function(){ return this.color; }
让一个类继承另一个类需要用许多行代码来完成。首先要像上面的示例一样创建一个构造函数。在构造函数中,调用超类的构造函数,并将参数name传给它。稍微解释一下这行代码,在使用new运算符的时候,系统会为你做一些事。它先创建一个空对象,然后调用构造函数,在此过程中这个空对象处于作用域链的最前端。面在BigBox函数中调用超类的构造函数时,你必须手工完成同样的任务"Box.call(this, name)"这条语句调用了Box构造函数,并且在此过程中让那个空对象(用“this”代表)处于作用域链的最前端,而name则被作为参数传入。
具体的实现过程如下:
var bigBox = new BigBox('bigbox', 'red'); bigBox.getName(); bigBox.getColor();
二、extend函数
为了简化声明可以将派生子类的整个过程包装在一个名为extend的函数中。它的作用与其他语言中的extend关键字类似,即基于一个给定的类结构创建一个新的类:
/* Extend function */ function extend(subClass, superClass){ var F = function(); F.prototype = superClass.prototype; // 只继承了superClass超级类中的方法并不包括属性(如果是定义在构造函数中,不在prototype里) subClass.prototype = new F(); // 因为F()函数只继承了超级类中prototype中的方法并没有其相关属性,所以subClass.prototype也只有superClass中的方法。 subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; if(superClass.prototype.constructor == Object.prototype.constructor){ superClass.prototype.constructor = superClass; } }
那上一个问题用extend()继承就变成如下形式:
/* Box Class */ function Box(name){ this.name = name; } Box.prototype.getName = function(){ return this.name; } /* BigBox Class */ function BigBox(name, color){ Box.call(this, name); // 向父类中传入参数且继承了父类中的相关属性 this.color = color; } extend(BigBox, Box); // 只继承了Box构造函数中的相关方法而不包括属性 BigBox.prototype.getColor = function(){ return this.color; }
注:其中“Box.call(this, name)”也可以等价为“BigBox.superclass.constructor.call(this, name)”,向构造函数中传入参数。这两种写法是同等的效果。如果在子类中又想调用父类中的某个方法也可以进行如下操作“BigBox.superclass.getName.call(this)”,向构造函数中的方法传入参数。因此有了superclass属性就可以直接调用超类中的方法。
三、原型继承
原型继承与类式继承截然不同。在我们谈到它的时候,最好忘掉自己关于类和实例的一切知识,只从对象的角度来思考。使用原型式继承时,并不需要用类来定义对象的结构,只需直接创建一个对象即可。这个对象随后可以被新的对象重用,这得得益于原型链查找的工作机制。该对象被称为原型对象(prototype object),这是因为它为其他对象应有的模样提供了一个原型。这正是原型式继承这个名称的由来。
下面我们仍然用类继承的例子来重新设计一个由原型继承的操作:
/* Box Prototype Ojbect */ var Box = { name : 'Box Object', getName : function(){ return this.name; } }
我们在这里并没用到一外名为Box的构造函数来定义类的结构,Box现在是一个对象字面量。它是所有创建的其他各种类Box对象的原型。其中也定义了所有类Box对象都要具备的属性和方法,并为它们提供了默认值。方法的默认值可能不会被改变,但属性的默认值一般都会被改变:
var smallBox = clone(Box); alert(smallBx.getName()); // 输出默认值“Box Object” smallBox.name = 'Small Box'; alert(smallBox.getName()); // 输出新值“Small Box”
clone函数我稍后再隆重推出,先卖个官司!clone函数可以用来创建新的类Box对象。它会创建一个空对象,而该对象的原型对象被设置成为Box。这意味着在这个新对象中查找某个属性时,如果找不到,那么查找过程会在其原型对象中继续进行。
你不必为创建BigBox而定义一个Box的子类,只要执行一次克隆即可:
/* BigBox Prototype Object */ var BigBox = clone(Box); BigBox.colors = []; Bigbox.getColors = function(){ retrun this.colors; }
然后你可以重新定义该克隆中的方法和属性。可以修改在Box中提供的默认值,也可以添加新的属性和方法。这样一来就伊娃了一个新的原型对象,你可以将其用于创建新的类BigBox对象。
var bigBox = []; bigBox[0] = clone(BigBox); bigBox[0].name = 'Big box 1'; bigBox[0].colors = ['red']; bigBox[1] = clone(BigBox); bigBox[1].name = 'Big box 2'; bigBox[1].colors = ['green']; bigBox[1].getName(); bigBox[1].getColors();
现在我们就来看看在这原型继承中起到关键性作用的clone函数:
/* Clone function */ function clone(object){ var F = function(){}; F.prototype = object; // 相当于如下定义:F.prototype = {name : 'Box Object', getName : function(){return this.name}} return new F; }
clone函数首先是创建了一个新的空函数F,然后将F的prototype属性设置为作为参数object传入的原型对象。prototype属性就是用来指向原型对象的,通过原型链接机制,它提供了到所有继承而来的成员的链接。该函数最后通过把new运算符作用于F创建出一个新对象,然后把这个新对象作为返回值返回。函数所返回的这个克隆结果是一个以给定对象为原型对象的空对象。
继承的主要好处表现在代码的重用方面。通过建立类或对象之间的继承关系,有些方法我们只需要定义一次即可。各种继承都有自己的优缺点。在内存效率比较重要的场合原型式继承(及clone函数)是最佳选择。如果与对象打交道的都是些只熟悉其他面向对象语言中的继承机制的程序员,那么最好使用类式继承(及extend函数)。这两种方法都适合球类 间差异较小的类层次体系。