by Mike Koss
March 26th, 2003

这是一篇,我个人认为最好的,Javascript面向对象编程的文章。翻译不好的地方,还望大家指正,谢谢。
如果您需要,可以访问下面的地址取得原文: http://mckoss.com/jscript/object.htm
在我的blog里,将会陆续推出这个理论的实践、源码。

介绍

大部分的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;<br><br>obj.x = 1;<br><br>obj.y = 2;

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

obj
x 1
y 2
prototype properties
constructor
function Object

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

对象的构造函数(Object Constructors)

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

function Foo()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x = 1;<br><br>&nbsp;&nbsp;&nbsp; this.y = 2;<br><br>}<br><br> <br><br>obj1 = new Foo;<br><br>

obj1
x 1
y 2
prototype properties
constructor function Foo

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

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

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

function Foo()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x = 1;<br><br>&nbsp;&nbsp;&nbsp; this.y = 2;<br><br>&nbsp;&nbsp;&nbsp; this.Bar = MyMethod;<br><br>}<br><br> <br><br>function MyMethod(z)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x += z;<br><br>}<br><br> <br><br>obj2 = new Foo;<br><br>

obj2
x 1
y 2
Bar function MyMethod
prototype properties
constructor function Foo

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

obj2.Bar(3);<br><br>

obj2
x 4
y 2
Bar function MyMethod
prototype properties
constructor function Foo

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

function Foo()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x = 1;<br><br>&nbsp;&nbsp;&nbsp; this.DoIt = FooMethod;<br><br>}<br><br> <br><br>function FooMethod()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x++;<br><br>}<br><br> <br><br>function Bar()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.z = 'Hello';<br><br>&nbsp;&nbsp;&nbsp; this.DoIt = BarMethod;<br><br>}<br><br> <br><br>function BarMethod()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.z += this.z;<br><br>}<br><br> <br><br>obj1 = new Foo;<br><br>obj2 = new Bar;<br><br>

obj1
x 1
DoIt function FooMethod
prototype properties
constructor function Foo
obj2
z Hello
DoIt function BarMethod
prototype properties
constructor function Bar

function Poly(obj)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; obj.DoIt();<br><br>}<br><br> <br><br>Poly(obj1);<br><br>Poly(obj2);<br><br>

obj1
x 2
DoIt function FooMethod
prototype properties
constructor function Foo
obj2
z HelloHello
DoIt function BarMethod
prototype properties
constructor function Bar

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

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

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

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

function Foo()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x = 1;<br><br>}<br><br> <br><br>Foo.prototype.y = 2;<br><br>obj = new Foo;<br><br>document.write('obj.y = ' + obj.y);<br><br>

obj.y = 2
obj
x 1
prototype properties
constructor function Foo

prototype
y 2
y 2

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

Foo.prototype.y = 3;<br><br>document.write('obj.y = ' + obj.y);<br><br>

obj.y = 3
obj
x 1
prototype properties
constructor function Foo

prototype
y 3
y 3

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

obj.y = 4;<br><br>Foo.prototype.y = 3;<br><br>

obj
x 1
y 4
prototype properties
constructor function Foo

prototype
y 3

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

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

function Foo()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x = 1;<br><br>}<br><br> <br><br>function Foo.prototype.DoIt()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.x++;<br><br>}<br><br>obj = new Foo;<br><br>obj.DoIt();

obj
x 2
prototype properties
constructor function Foo

prototype
DoIt function Foo.prototype.DoIt
DoIt function Foo.prototype.DoIt

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

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

function TextObject(st)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.st = st;<br><br>&nbsp;&nbsp;&nbsp; this.fVisible = true;<br><br>}<br><br> <br><br>function TextObject.prototype.Write()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; document.write('<br><br>' + this.st);<br><br>}<br><br> <br><br>function ItalicTextObject(st)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.st = st;<br><br>}<br><br> <br><br>ItalicTextObject.prototype = new TextObject('x');<br><br> <br><br>ItalicTextObject.prototype.Write = ITOWrite;<br><br>function ITOWrite()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; document.write('<br><br><em>' + this.st + '</em>');<br><br>}<br><br> <br><br>obj1 = new TextObject('Hello, mom');<br><br>obj2 = new ItalicTextObject('Hello, world');<br><br>obj1.Write();<br><br>obj2.Write();<br><br>


Hello, mom
Hello, world
obj1
st Hello, mom
fVisible true
prototype properties
constructor function TextObject

prototype
Write function TextObject.prototype.Write
Write function TextObject.prototype.Write
obj2
st Hello, world
prototype properties
constructor function TextObject

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

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

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

function Function.prototype.DeriveFrom(fnBase)<br><br><br><br>{<br><br><br><br>&nbsp;&nbsp;&nbsp; var prop;<br><br><br><br><br><br><br><br>&nbsp;&nbsp;&nbsp; if (this == fnBase)<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert("Error - cannot derive from self");<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br><br><br><br><br><br><br>&nbsp;&nbsp;&nbsp; for (prop in fnBase.prototype)<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (typeof(fnBase.prototype[prop]) == "function" &amp;&amp; !this.prototype[prop])<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.prototype[prop] = fnBase.prototype[prop];<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br><br><br><br><br><br><br>&nbsp;&nbsp;&nbsp; this.prototype[fnBase.StName()] = fnBase;<br><br><br><br>}function Function.prototype.StName()<br><br><br><br>{<br><br><br><br>&nbsp;&nbsp;&nbsp; var st;<br><br><br><br><br><br><br><br>&nbsp;&nbsp;&nbsp; st = this.toString();<br><br><br><br>&nbsp;&nbsp;&nbsp; st = st.substring(st.indexOf(" ")+1, st.indexOf("("))<br><br><br><br><br><br><br><br>&nbsp;&nbsp;&nbsp; return st;<br><br><br><br>}function TextObject(st)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.st = st;<br><br>&nbsp;&nbsp;&nbsp; this.fVisible = true;<br><br>}<br><br> <br><br>function TextObject.prototype.Write()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; document.write('<br><br>' + this.st);<br><br>}<br><br> <br><br>function TextObject.prototype.IsVisible()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; return this.fVisible;<br><br>}<br><br> <br><br>function ItalicTextObject(st)<br><br>{<br><br>&nbsp;&nbsp;&nbsp; this.TextObject(st);<br><br>}<br><br> <br><br>ItalicTextObject.DeriveFrom(TextObject);<br><br> <br><br>function ItalicTextObject.prototype.Write()<br><br>{<br><br>&nbsp;&nbsp;&nbsp; document.write('<br><br><em>' + this.st + '</em>');<br><br>}<br><br> <br><br>obj1 = new TextObject('Hello, mom');<br><br>obj2 = new ItalicTextObject('Hello, world');<br><br>obj1.Write();<br><br>obj2.Write();<br><br>


Hello, mom
Hello, world
obj1
st Hello, mom
fVisible true
prototype properties
constructor function TextObject

prototype
Write function TextObject.prototype.Write
IsVisible function TextObject.prototype.IsVisible
IsVisible function TextObject.prototype.IsVisible
Write function TextObject.prototype.Write
obj2
st Hello, world
fVisible true
prototype properties
constructor function ItalicTextObject

prototype
Write function ItalicTextObject.prototype.Write
IsVisible function TextObject.prototype.IsVisible
TextObject function TextObject

prototype
Write function TextObject.prototype.Write
IsVisible function TextObject.prototype.IsVisible
IsVisible function TextObject.prototype.IsVisible
TextObject function TextObject

prototype
Write function TextObject.prototype.Write
IsVisible function TextObject.prototype.IsVisible
Write function ItalicTextObject.prototype.Write

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

在Javascript中使用面向对象的编程相关推荐

  1. 如何理解并学习javascript中的面向对象(OOP)

    本文不适合javascript初学者看(javascript水平还停留在函数级别的朋友,看了会觉得很晕的).如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的 ...

  2. javascript 中的面向对象实现 如何封装

    javascript 是一门很灵活的语言,也是一门有缺陷的语言. 比如我们今天要谈的,如何用面向对象的手法来封装javascript ,javascript是没有类的概念的. 所以今天谈到的封装,其实 ...

  3. JavaScript中OOP——面向对象中的继承/闭包

      前  言  OOP  JavaScript中OOP-->>>面向对象中的继承/闭包 1.1面向对象的概念 使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法.      ...

  4. html5学习笔记---05.JavaScript 中的面向对象,继承和封装

    05.JavaScript 中的面向对象 a.创梦技术qq交流群:CreDream:251572072 a.JavaScript 是一种基于对象的语言   类:JavaScript 对象很抽象,所以下 ...

  5. 17、Java中的面向对象的编程思想

    Java中的面向对象的编程思想 作者:韩茹 公司:程序咖(北京)科技有限公司 程序咖:IT职业技能评测平台 网址:https://www.chengxuka.com 任务 1. 面向对象 2. 面向过 ...

  6. javascript中的面向对象理解(一)

    一.注意:提到"面向对象"这一概念,众所周知,javascript中的面向对象思想与其他的编程语言(例如:PHP.Java等)是有着很大区别的.因此,我们先复习下,传统意义上,面向 ...

  7. javascript中的面向对象_面向对象和函数式编程的本质区别

    编程的本质 当写过许许多多程序后,接触了那么多编程模式.设计模式.框架.语言.算法.数据结构以后,就会发现编程的本质万变不离其宗就是,操纵一坨数据.当然操纵的方式有许多,存储的方式也五花八门,但是本质 ...

  8. JavaScript中的面向对象程序设计

    本文内容目录顺序: 1.Object概念讲述: 2.面向对象程序设计特点: 3.JavaScript中类和实例对象的创建: 4.原型概念: 5.原型API: 6.原型对象的具体使用:7.深入理解使用原 ...

  9. JavaScript中的面向对象--对象创建

    JavaScript高级程序设计第3版总结p156 1.JavaScript中的对象 首先,ECMAScript 中函数实际上是对象.每个函数都是 Function 类型的实例,而且都与其他引用类型一 ...

最新文章

  1. 人脸检测--Scale-Aware Face Detection
  2. 用JS脚本进行页面元素控制
  3. svn 部署问题总结
  4. C++11新特性- for语句
  5. Superset-Quick-Start-Guide翻译(没有完成)
  6. delphi pi怎么得到?
  7. 设备划分冲突域和广播域
  8. web前端 day11今日大纲
  9. ISV的想法,用友全都懂
  10. python之excel读写报表统计入门
  11. 智联招聘 爬虫职位信息的爬取
  12. 双系统下ubuntu自动挂载windows盘
  13. 非匀质化资金池——为什么资产 NFT 化是 DeFi 的必经之路
  14. 思科华为设备STP、RSTP配置命令对比
  15. 校园快递管理系统-JAVA.JSP【数据库设计、论文、源码、开题报告】
  16. vue+element简单实现商城网站首页,模仿电商商城
  17. 深度强化学习系列(14): A3C算法原理及Tensorflow实现
  18. 数据传输的常用格式(转载博主「落花别有意 流水似无情」)
  19. PC微信hook api接口文档
  20. COBOL--02--案例1

热门文章

  1. C语言基础知识(自己做个笔记,云储存一下)
  2. 用计算机计算成品率计算公式,计算机专业英语+单词+部分习题.doc
  3. 触摸矫正+android,android触摸矫正解方程
  4. python怎样判断一个文件是否存在_python如何判断一个文件是否存在
  5. python argparse模块_Python argparse模块应用实例解析
  6. Pyhton,OpenCV对象检测之——Haar级联人脸及眼睛检测
  7. gym102443 D.Guess the Path
  8. C语言结束输入(两种方法)
  9. Matlab中的lsqcurvefit函数的使用
  10. Road-SLAM:基于道路标线车道级精度SLAM