key 发表于 14-6-2009 01:10:51

JavaScript O-O-P之非完全手册

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

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

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

key 发表于 14-6-2009 01:14:18

Encapsulation

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

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

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

key 发表于 14-6-2009 01:19:06

Inheritance

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

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

key 发表于 14-6-2009 01:31:52

入门篇:对象的定义

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

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

myObject.x = 10;
myObject.y = "hello";
myObject.foo = function() {
alert('y is ' + this.y);
}这样就做成一个简单的对象。可以采用直接定义的方法来简单上面的程序:var myObject =
{
x : 10,
y : 'hello',
foo : function() {
    alert('y is ' + this.y);
}
}

key 发表于 14-6-2009 01:38:10

构造函数与对象再定义

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

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

key 发表于 14-6-2009 01:46:21

原型定义与类定义模拟

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

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

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

MyObject.prototype.foo = function() {
alert('y is ' + this.y);
}

key 发表于 14-6-2009 01:50:04

神奇的动态属性添加特性

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

var x = 'hello';
alert(x.endsWith('o'));

key 发表于 14-6-2009 01:58:08

类属性与对象属性

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

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

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

key 发表于 14-6-2009 02:05:07

原型属性的读与写

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

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

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

key 发表于 14-6-2009 02:16:11

继承模拟

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

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

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

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

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


function BaseObject() {
}

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

function DerivedObject() {
}

DerivedObject.prototype = new BaseObject();
DerivedObject.prototype.constructor = DerivedObject;

key 发表于 14-6-2009 02:23:23

Override,方法覆盖与溯回

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

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

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

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

key 发表于 14-6-2009 03:04:31

typeof

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

但问题不是没有解决的方案。如果你的类并没有覆盖Object的toString方法,
那么用toString方法得到的名称中“含有”了这个类的类名:function TypeOf(x) {
var t = typeof x;
if(t != 'object') return t;
var c = Object.prototype.toString.apply(x);
c = c.substring(8, c.length - 1);
return c;
}代码来自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 , where type is the object type or the name of the constructor function that created the object.

stgeorge 发表于 14-6-2009 14:32:52

哈哈,key有空讲讲闭包吧,用了N年的Javascript,还没有仔细看过呢。

kaile 发表于 14-6-2009 17:19:57

好,学习一下

coredump 发表于 14-6-2009 20:36:28

well done:victory: :zan

figaro 发表于 16-6-2009 02:17:14

很好,以后慢慢看
页: [1]
查看完整版本: JavaScript O-O-P之非完全手册