找回密码
 FreeOZ用户注册
查看: 1708|回复: 15
打印 上一主题 下一主题

[论坛技术] JavaScript O-O-P之非完全手册

[复制链接]
跳转到指定楼层
1#
发表于 14-6-2009 01:10:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
判断题:JavaScript是一种 Object Oriented 开发语言?
答案:错

Javascript是一种Object-based开发语言。同样的概念可见到VB,类似的概念可见到PostgreSQL。
但因为object的关系,JS能做很多面向对象的设计和实现。

本文的目的就是讨论使用JS进行面向对象程序设计/实现时应该使用的技巧和注意问题。
请注意,我不是一个JS开发人员,而我参考的是一本可能有点过时的书:主要是基于Javascript 1.3的书:
Javascript: The Definitive Guide 第四版。
回复  

使用道具 举报

2#
 楼主| 发表于 14-6-2009 01:14:18 | 只看该作者

Encapsulation

OOP有两个重要概念:封装,继承

而封装,我们可以看成两个部分的内容:一是把属性、方法组合在一起,
二是对这些方法做访问控制。

Javascript实现了第一种封装,而没有提供有效的访问控制,所有的
属性和方法都是public的。
回复  

使用道具 举报

3#
 楼主| 发表于 14-6-2009 01:19:06 | 只看该作者

Inheritance

继承是OOP另一个重要特性。这个特性让OOP程序实现大大减少重复编码,
同时也成为各种复杂架构的基础。

Javascript提供特殊的继承实现。与其说是继承,不如说是继承模拟。
理论上说,所有的Javascript对象(它没有类的实质概念)都只有一个直接
父对象,就是Object。但通过Javascript提供的prototype机制,通过指定
一个对象的prototype为另一个对象的实例,从而达到重用后一个对象的属性
和方法。所以,Javascript的“继承”模拟,个人认为是直接冲着代码复用而来的。
回复  

使用道具 举报

4#
 楼主| 发表于 14-6-2009 01:31:52 | 只看该作者

入门篇:对象的定义

讨论了两个OOP主题后,进入务实内容。首先来看JS对象的定义。

实质上说,Javascript并没有提供类的概念,但因为它提供了Object作为基础
数据类型,再利用Javascript提供的动态属性添加机制,以及函数可做为属性的机制,
从而构造出/模拟出一个对象编程环境:
  1. var myObject = new Object();

  2. myObject.x = 10;
  3. myObject.y = "hello";
  4. myObject.foo = function() {
  5.   alert('y is ' + this.y);
  6. }
复制代码
这样就做成一个简单的对象。可以采用直接定义的方法来简单上面的程序:
  1. var myObject =
  2. {
  3.   x : 10,
  4.   y : 'hello',
  5.   foo : function() {
  6.     alert('y is ' + this.y);
  7.   }
  8. }
复制代码
回复  

使用道具 举报

5#
 楼主| 发表于 14-6-2009 01:38:10 | 只看该作者

构造函数与对象再定义

《入门篇》中提到的方法固然有效,但不高效。如果内存中
需要N个MyObject这样的对象实例,那单单定义对象就可以让
我们累死。就象传说中那个学写“万”字的富贵少爷,学了一、二、三,
然后被叫到写个请帖请一位“万老爷”来吃喜酒。

于是我们要用到构造函数,从而使我们能轻而易举地实现:
  1.   var obj = new MyObject();
复制代码
  1.   var obj = new MyObject(10, 'hello');
  2.   var obj2 = new MyOjbect(11, 'world');
复制代码
我们可以这样做:
  1. function MyObject(xv, yv)
  2. {
  3.   this.x = xv;
  4.   this.y = yv;
  5.   this.foo = function() {
  6.     ....
  7.   }
  8. }
复制代码
注意,JS没有强类型检测,这是语言固有的问题,咱只能忍了。
回复  

使用道具 举报

6#
 楼主| 发表于 14-6-2009 01:46:21 | 只看该作者

原型定义与类定义模拟

前面我们说到,javascript实质上没有类这个东西。没有类,怎样OOP呀?
同学,人家都说了自己不是OOP语言,你这么执着做什么呢?

但不是OOP,可以模拟。js就是用prototype来模拟类的定义。

且看《构造函数》篇,如果每一个object都用我们的构造函数去定义
一个自己的对象,你会发现所有的x, y, foo都满地开发。对于从Java, C++
走过来的人,x和y那就算了,但foo还要满街跑,就太不象样了。
原型正好解决了这个问题:
  1. function MyObject(xv, yv)
  2. {
  3.   this.x = xv;
  4.   this.y = yv;
  5. }

  6. MyObject.prototype.foo = function() {
  7.   alert('y is ' + this.y);
  8. }
复制代码
回复  

使用道具 举报

7#
 楼主| 发表于 14-6-2009 01:50:04 | 只看该作者

神奇的动态属性添加特性

通过了解prototype,再结合js的动态属性添加语言特性,我们可以做出很神奇的事。
比如我们想要扩展String类,方法很简单:
  1. String.prototype.endsWith = function(c) {
  2.   return c == this.charAt(this.length - 1);
  3. }

  4. var x = 'hello';
  5. alert(x.endsWith('o'));
复制代码
回复  

使用道具 举报

8#
 楼主| 发表于 14-6-2009 01:58:08 | 只看该作者

类属性与对象属性

所谓类属性,在Java或C++中表现为静态成员,包括静态数据成员和静态成员函数(方法)。
而在js中是没有这支歌仔的。

js还是走它的函数路子。它的类是通过函数,或叫构造函数来模拟,它的类属性自然就通过
构造函数(的名称)来关联模拟:
  1. function MyObject(){
  2.   ...
  3. }

  4. MyObject.VA = 100;
  5. MyObject.doit = function() {
  6.   alert('tell lora i love her');
  7. }
复制代码
这里要搞清楚原型属性与类属性的关系。事实上,通过原型定义的属性还是实例的属性,
可以通过this来访问,但类属性则和this没有关系(C++/Java程序员要注意了),
你通过this是得不到类属性的,白跑一趟呐。
回复  

使用道具 举报

9#
 楼主| 发表于 14-6-2009 02:05:07 | 只看该作者

原型属性的读与写

不知道多少人看过《北京人的乐与路》,我是没有看过的,不过写完个题目,
我就想起这个电影名而已。

原型属性可以被对象实例通过“点”号进行读出,通过this读出,这个已经不需要
争论了(不然,弄来干嘛?)。原型的每一个属性,在内存中只有一个“实例”,
这可能是js作为网页动态语言的一个考虑吧。

如果利用实例,对某个在原型中定义了的属性进行写操作。这样的操作并不会改变
原型的属性值,而是建立一个专属于该实例的属性,设置专门的值,并覆盖原来的
原型属性。简单地说,如果大家都是鸡蛋,你也只需要鸡蛋,那就直接读原型;
如果你偏要弄个鸭蛋在手,那就需要你自己扛着了。这思想好懂,知道就行了。
回复  

使用道具 举报

10#
 楼主| 发表于 14-6-2009 02:16:11 | 只看该作者

继承模拟

js没有继承这回事,但可以模拟。虽然我的参考书不提倡大量使用这种继承模拟的事儿。
我暂时还没有看那本《Javascript DesignPatterns》,不过凭感觉,可能会大量使用
composit来代替继承做事吧,有空看看再说。

anyway,作为OOP,即使不用继承,弄清楚也是好事。

说到继承又是和prototype相关。这哥儿可是够义气的了。具体的做法是:
通过指定prototype为父类的一个实例,从而复用父类的所有属性和方法。

这道理不难明白:我们知道一个实例可以通过this访问对象原型的所有方法,也可以
通过自定义一些属性进行添加或覆盖,这就达到了扩展和override的模拟了。

但这样一来,还需要注意在prototype中重新指定contructor属性,使之指向当前类的构造
函数:


  1. function BaseObject() {
  2. }

  3. BaseObject.prototype.foo = function() {
  4.   alert('hello world');
  5. }

  6. function DerivedObject() {
  7. }

  8. DerivedObject.prototype = new BaseObject();
  9. DerivedObject.prototype.constructor = DerivedObject;
复制代码
回复  

使用道具 举报

11#
 楼主| 发表于 14-6-2009 02:23:23 | 只看该作者

Override,方法覆盖与溯回

不知道有谁有印象我在这里问过Java的一个问题:如果A的方法被子类B覆盖,到了B的子类C时,
如果想用A的这个方法,有什么办法?答案是Java从语言的角度来否定这种恶心的设计。

OK,我们不谈设计,我们谈语言特性。

在js中,我们同样可以模拟overriding,方法就是在prototype中重写一个同名的函数。
这个不难理解,也无需解释了。

现在的问题是,我们如何找回父类,或父类的父类的。。。父类的这个同名函数?
答案是采用原型apply:
  1. Object.prototype.toString.apply(myobject);
复制代码
这里假设myobject是Object的一个子类的实例,而这个子类把toString方法override掉了。
回复  

使用道具 举报

12#
 楼主| 发表于 14-6-2009 03:04:31 | 只看该作者

typeof

自定义的类实例,是否能通过typeof显示出类名?答案是否定的。

但问题不是没有解决的方案。如果你的类并没有覆盖Object的toString方法,
那么用toString方法得到的名称中“含有”了这个类的类名:
  1. function TypeOf(x) {
  2.   var t = typeof x;
  3.   if(t != 'object') return t;
  4.   var c = Object.prototype.toString.apply(x);
  5.   c = c.substring(8, c.length - 1);
  6.   return c;
  7. }
复制代码
代码来自Javascript: The Definitive Guide

这里需要知道,对于用户自定义的类,得到的还是Object,虽然Javascript的文档有称:

By default, the toString method is inherited by every object descended from Object. You can override this method for custom objects that you create. If you do not override toString in a custom object, toString returns [object type], where type is the object type or the name of the constructor function that created the object.
回复  

使用道具 举报

13#
发表于 14-6-2009 14:32:52 | 只看该作者
哈哈,key有空讲讲闭包吧,用了N年的Javascript,还没有仔细看过呢。
回复  

使用道具 举报

14#
发表于 14-6-2009 17:19:57 | 只看该作者
好,学习一下
回复  

使用道具 举报

15#
发表于 14-6-2009 20:36:28 | 只看该作者
well done
回复  

使用道具 举报

16#
发表于 16-6-2009 02:17:14 | 只看该作者
很好,以后慢慢看
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 23-2-2025 22:47 , Processed in 0.021883 second(s), 31 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表