javascript面向对象

来源:互联网 发布:货币战争知乎 编辑:程序博客网 时间:2024/05/02 06:12

大部分的Javascript的编写者,都只是把它做为简单的脚本引擎,来创建动态的Web页面。同时Web设计人员开始使用在IE浏览器中定义的对象模型,来处理Web页面的内容。但是大多数的开发者并没有认识到Javascript在其自身就具有强大的面向对象的功能。当不使用强类型的时候(变量不必先声明后使用),这种解析性的语言,可以巧妙的达成面向对象(object-oriented)的功能,包括:

  • 封装 (Encapsulation)
  • 多台 (Polymorphism )
  • 继承 (Inheritance)

虽然,通过一系列的范例(对于好奇的读者,这些范例片断代码是很生动的),我将会阐述对象在Javascript中,对象是如何被使用,并且如何实现面向对象的。

简单对象(Simple Objects)

在Javascript中,最简单的可构建的对象,就是机制内建的Object对象。在Javascript中,对象是指定名称的属性(property)的集合。做为解析性语言,Javascript允许给一个对象创建任意个属性,在任何时间(不像C++,它的属性是可以在任何时间添加给对象。它们并不需要事先在对象的声明(definition)或者构造(constructor)中,进行定义)。

所以,举例来说,我们可以创建一个对象,然后添加一系列的属性给它,就像这样:

obj = new Object;

obj.x = 1;

obj.y = 2;

这里,Javascript对象,可以用图形表示成这样的结构:

objx1y2prototype propertiesconstructor
function Object

另外需要注意的是,我们创建的x和y属性, 我们的对象默认有一个属性constructor t他指向一个Javascript内部对象函数(funciton)。 (译者注:prototype,原型在后文会有进一步的说明)

对象的构造函数(Object Constructors)

对于要定义的对象类型,Javascript允许我们自己给对象类型定义构造函数:

function Foo()

{

    this.x = 1;

    this.y = 2;

}



obj1 = new Foo;

obj1x1y2prototype propertiesconstructorfunction Foo

这里要说明的是,我们可以创建任意多个Foo类型的对象实例,它们也都将分别初始化自己的x和y属性为1和2。

简单的方法的的实现(A Simple Method Implementation)

为了封装对象的行为功能,向调用者隐藏执行过程,我们需要给对象创建方法(method)。Javascript允许你将任意一个函数(function)分配给对象的一个属性。当我们使用 obj.Function 的语法调用函数的时候,将把函数原来定义this 的指向,当前这个对象(就像它在构造函数中的那样)。

function Foo()

{

    this.x = 1;

    this.y = 2;

    this.Bar = MyMethod;

}



function MyMethod(z)

{

    this.x += z;

}



obj2 = new Foo;

obj2x1y2Barfunction MyMethodprototype propertiesconstructorfunction Foo

现在,我们简单的调用一下,做为对象的方法的Bar函数:

obj2.Bar(3);

obj2x4y2Barfunction MyMethodprototype propertiesconstructorfunction Foo

所以,你可以方便的给对象定义构造函数和方法,使其对调用者而言,隐藏它的实现过程。同样的,因为,Javascript不是强类型的,所以,我们可以通过定义有相同名字的方法的对象,来简单的实现多台性(polymorphism)。

function Foo()

{

    this.x = 1;

    this.DoIt = FooMethod;

}



function FooMethod()

{

    this.x++;

}



function Bar()

{

    this.z = 'Hello';

    this.DoIt = BarMethod;

}



function BarMethod()

{

    this.z += this.z;

}



obj1 = new Foo;

obj2 = new Bar;

obj1x1DoItfunction FooMethodprototype propertiesconstructorfunction Fooobj2zHelloDoItfunction BarMethodprototype propertiesconstructorfunction Bar
function Poly(obj)

{

    obj.DoIt();

}



Poly(obj1);

Poly(obj2);

obj1x2DoItfunction FooMethodprototype propertiesconstructorfunction Fooobj2zHelloHelloDoItfunction BarMethodprototype propertiesconstructorfunction Bar

使用原型实现方法(Using Prototypes to Implement Methods)

试想一下,这使很笨的办法,每次我们都要创建名称没有使用意义的方法函数,然后在构造函数里,把它们分配给每个方法属性。其实,我发现使用Javascript的原型(prototype)机制,是更为直接的方法。 

每个对象,可以参照一个原型对象,原型对象包含有自己的属性。它就好比是一个对象定义的备份。当代码,引用一个属性的时候,它并不存在于对象本身里,那么Javascript将会自动的在原型的定义中查找这个属性。而且,事实上,一个对象的原型对象又可以参照另外一个原型对象,就这样以链式最终关联到基类对象的构造函数。(译者注:对于DOM对象等系统的对象,原型对象可以修改,但是不可以赋值改变的,只有自定义对象可以。)这是template模型(译者注:模板方法,《设计模式》中行为模式的一种),它可以简化我们对方法的定义,同时也可以产生强大的继承机制。

在Javascript中,原型对象是被分配给构造函数的。所以,为了修改对象的原型,必须首先修改构造函数的原型对象的成员。然后,当对象从构造函数被构造的时候,对象将会引用到构造函数的原型。 

 

function Foo()

{

    this.x = 1;

}



Foo.prototype.y = 2;

obj = new Foo;

document.write('obj.y = ' + obj.y);

obj.y = 2
objx1prototype propertiesconstructorfunction Foo prototypey2y2

即使我们并没有直接的把y属性分配给obj,obj对象仍然有一个y属性。当我们引用obj.y的时候,Javascript实际返回obj.constructor.prototype.y的引用。我们可以肯定的是,原型的值的改变,也将会反映到对象中。 

Foo.prototype.y = 3;

document.write('obj.y = ' + obj.y);

obj.y = 3
objx1prototype propertiesconstructorfunction Foo prototypey3y3

我们也可以发现,一旦我们初始化一个属性的“私有”(private )的值,存放在原型中的值并不会收到影响:

obj.y = 4;

Foo.prototype.y = 3;

objx1y4prototype propertiesconstructorfunction Foo prototypey3

原型方法的命名(Prototype Method Naming)

我发现了可以直接定义类的原型的方法的语句,而不需要单独的函数的名称:

function Foo()

{

    this.x = 1;

}



function Foo.prototype.DoIt()

{

    this.x++;

}

obj = new Foo;

obj.DoIt();objx2prototype propertiesconstructorfunction Foo prototypeDoItfunction Foo.prototype.DoItDoItfunction Foo.prototype.DoIt

基于原型的子类继承(Prototype-based Subclassing )

一旦可以建立原型对象链,我们就可以使用它做为对象的子类的类型。这个方法要注意的是,我们创建了一个基类对象的实例,并把它做为我们的类的构造函数的原型对象。这么做,我们所创建的所有的对象,将继承基类对象的所有成员和(方法)。但是要注意,基类的构造函数只会被调用一次(译者注:从基类到子类的构造函数都是唯一的,即基类的构造函数)。这不像C++,基类的构造函数,对于每个继承的子类,都可以分别的调用。在后面,我将展示,当独立的构造函数被需要的时候,另外一种可选的方式来创建继承类。
function TextObject(st)

{

    this.st = st;

    this.fVisible = true;

}



function TextObject.prototype.Write()

{

    document.write('

' + this.st);

}



function ItalicTextObject(st)

{

    this.st = st;

}



ItalicTextObject.prototype = new TextObject('x');



ItalicTextObject.prototype.Write = ITOWrite;

function ITOWrite()

{

    document.write('

' + this.st + '');

}



obj1 = new TextObject('Hello, mom');

obj2 = new ItalicTextObject('Hello, world');

obj1.Write();

obj2.Write();


Hello, mom
Hello, world
obj1stHello, momfVisibletrueprototype propertiesconstructorfunction TextObject prototypeWritefunction TextObject.prototype.WriteWritefunction TextObject.prototype.Writeobj2stHello, worldprototype propertiesconstructorfunction TextObject prototypeWritefunction TextObject.prototype.WritefVisibletrueWritefunction ITOWrite
这个结构存在两个问题。一个是,当每次构造继承的类的时候,基类的构造函数都不会被调用。假如,构造函数不做太多的事情,只是初始化一些成员变量为静态的值,这个问题就不是太明显了。第二个,注意,我将不能使用"function Obj.prototype.Method"的方式,来定义继承类的成员。这是因为,对于构造函数来说,我要把这些方法的定义,放入新创建的原型对象,而不是添加到,默认的原型对象。 

另一种子类继承方式(An Alternate Subclassing Paradigm)

我们可以提出一种方法,更类似于反映C++类的概念和子类的定义,以及从子类反向存取基类的纯原型链的风格。它需要添加新的方法DeriveFrom给基类。

function Function.prototype.DeriveFrom(fnBase)



{



    var prop;







    if (this == fnBase)



        {



        alert("Error - cannot derive from self");



        return;



        }







    for (prop in fnBase.prototype)



        {



        if (typeof(fnBase.prototype[prop]) == "function" && !this.prototype[prop])



            {



            this.prototype[prop] = fnBase.prototype[prop];



            }



        }







    this.prototype[fnBase.StName()] = fnBase;



}function Function.prototype.StName()



{



    var st;







    st = this.toString();



    st = st.substring(st.indexOf(" ")+1, st.indexOf("("))







    return st;



}function TextObject(st)

{

    this.st = st;

    this.fVisible = true;

}



function TextObject.prototype.Write()

{

    document.write('

' + this.st);

}



function TextObject.prototype.IsVisible()

{

    return this.fVisible;

}



function ItalicTextObject(st)

{

    this.TextObject(st);

}



ItalicTextObject.DeriveFrom(TextObject);



function ItalicTextObject.prototype.Write()

{

    document.write('

' + this.st + '');

}



obj1 = new TextObject('Hello, mom');

obj2 = new ItalicTextObject('Hello, world');

obj1.Write();

obj2.Write();


Hello, mom
Hello, world
obj1stHello, momfVisibletrueprototype propertiesconstructorfunction TextObject prototypeWritefunction TextObject.prototype.WriteIsVisiblefunction TextObject.prototype.IsVisibleIsVisiblefunction TextObject.prototype.IsVisibleWritefunction TextObject.prototype.Writeobj2stHello, worldfVisibletrueprototype propertiesconstructorfunction ItalicTextObject prototypeWritefunction ItalicTextObject.prototype.WriteIsVisiblefunction TextObject.prototype.IsVisibleTextObjectfunction TextObject prototypeWritefunction TextObject.prototype.WriteIsVisiblefunction TextObject.prototype.IsVisibleIsVisiblefunction TextObject.prototype.IsVisibleTextObjectfunction TextObject prototypeWritefunction TextObject.prototype.WriteIsVisiblefunction TextObject.prototype.IsVisibleWritefunction ItalicTextObject.prototype.Write

我们还得到了一个额外的好处,那就是,我们可以从多个基类进行继承(多重的继承)。

原创粉丝点击