OOP 诡异教程 (上)

氷の鋭

2007-06-08 12:40:07 来自: 氷の鋭(神不为者,人为之)

本文分上、下两篇,站在一个难以名状的角度上研究了 JavaScript 语言中面向对象机制的起源、内涵和发展,带领读者从原始森林走向高楼大厦。文章作者 lichray 是个 ECMAScript 的狂热追随者,mozilla.org 邮件列表里的无名潜水员。
文章中使用了 Rhino 解释器,行开头有 "js>" 表示那是输入,输入下一行没有这个标记的表示解释器回馈消息。
PS: 读懂本文需要对 JavaScript 闭包和逃逸变量有较深入的了解。

一. 对象和消息
考虑一下我们平常怎么说话的。我们叫某某人做某事,用下面的句式:
forest run!
其中"!"是语气的标志,对于编程语言来说是没有意义的,全部换成".":
forrest run.
不知道如果我告诉大家上面这句话就是 Smalltalk 语言中一个合法语句大家会怎么想。好了,不谈这个。这样我们就得到了一种语法,"宾"谓结构:
ObjectVerb ::
  Object Verb.
如果让它支持多个 Verb,比如
forrest run, jump, stop.
可以扩展成这样:
ObjectVerb ::
  Object VerbList.
VerbList ::
  Verb
  Verb , VerbList
很明显,对于 JavaScript 来说,上面的 BNF 不可能和任何一个产生式匹配。问题出在哪儿?我们要帮 JavaScript 指定,谁是 Object,谁是 Verb。鉴于 Object 只有一个,Verb 有多个,我们可以用括号来区分它们,然后把最后那个句号去掉:
ObjectVerb ::
  Object ( VerbList )
这样上面的那句话就变成了下面的形式:
forrest (run, jump, stop)
很像函数调用,是吧?不过还有一个问题,现在这些 Verb(s) 对于 JavaScript 来说是“裸词”(Perl 语),我们可以避开再去定义这些标识符,用字符串代替;最后再说明一下 Object 是什么:
forrest ('run', 'jump', 'stop')
那么现在我们第一个“模仿”自然语言的程序版本出现了,加上下面针对 JavaScript 的文法:
Object ::
  Identifier
Verb ::
  StringLiteral

二. 实现消息传递
有了文法,一切都好办。看得出来,我们下面的工作是定义能创建一个新 Object 的函数,函数中有一些动作,产生的新 Object 是一个能处理这些消息的函数。创建 Forrest Gump 的函数还可以创建 Tom,Mike 等等;他们都是 People:
function People () {
  function run () {
    print("I'm running!")
  }
  function jump () {
    print("I'm jumping!")
  }
  function stop () {
    print("I can't stop!")
  }  
  return (function (verb) {
    switch (verb) {
      case 'run': run(); break
      case 'jump': jump() ;break
      case 'stop': stop() ;break
    }
  })
}
为了简单起见还可以把返回的那个函数写成这样:
    (function (verb) {
      eval(verb)();
    }
  })
Ok。现在我们来试一试这个智商低于 85 的 Forrest Gump 怎么样:
js> forrest = People()
js> forrest('run')
I'm running!
js> forrest('jump')
I'm jumping!
js> forrest('stop')
I can't stop!
事情就是这样。我们成功地创造了对象,还让他做动作、说话。
不过,这个实现并不是我们上文中最后一个文法所指出的。它不支持连续发送指令。改一改。要加入顺序执行指令的办法:
function People () {
  function run () {
    print("I'm running!")
  }
  function jump () {
    print("I'm jumping!")
  }
  function stop () {
    print("I can't stop!")
  }
  function _do_verbs_ (verblist) {
    for (var i=0; i < verblist.length; i++)
      eval(verblist[i]).call()
  }
  return (function () {
    _do_verbs_(arguments)
  })
}
这下似乎比较像样了:
js> forrest = People()
js> forrest('jump','run','jump','stop')
I'm jumping!
I'm running!
I'm jumping!
I can't stop!

三. 利用消息传递处理状态
什么是状态?我们在进行面向对象编程时,把状态表示为对象的一组数据,我们称之为“属性(property)”。在我们的消息传递编程风格中,可以直接把这些数据堆到产生对象的那个函数中去。下面给 Forrest 加入一个状态,Forrest 口袋里的钱。先得声明原先有多少钱:
forrest = People(1000)
然后,我们希望可以执行这样的代码,让 forrest 支出 200 美元:
forrest('pay', 200)
但很明显,我们无法分清 200 是 Verb 还是 'pay' 所要求的数据。我们只得简化文法,只允许一次发送一个消息,以保全我们的脑细胞:
forrest('pay')(200)
也就是说,我们需要让 forrest('pay') 这一表达式返回一个能改变状态的函数,而不仅仅是调用函数来显示一句话。也就是说,如果我们想让 Forrest 急得跳起来,我们先得跳起来:
forrest('jump')()
新时代的 Forrest 实现如下(省略了一点多余的代码):
function People (money) {
  //var money = money
  function pay (dollars) {
    money -= dollars
  }
  function restMoney () {
    return money
  }
  function run () {
    print("I'm running!")
  }
  return (function (verb) {
    return eval(verb)
  })
}
试一下。先支出 200 美元,然后看看他还剩多少钱:
js> forrest=People(1000)
js> forrest('restMoney')()
1000
js> forrest('pay')(200)
js> forrest('restMoney')()
800
当然,我们的 Forrest 还可以赚钱。下面这个版本比较彻底地说明了消息传递编程风格的一切。可以直接修改钱之后,我们可以不需要在创建 Object 的时候就说明原有多少钱;当然,使用注释中的版本更自然:
function People (/* money */) {
  var money = 0; // var money = money ? money : 0;
  function setMoney (dollars) {
    money = dollars
  }
  function addMoney (dollars) {
    money += dollars
  }
  function pay (dollars) {
    money -= dollars
  }
  function restMoney () {
    return money
  }
  return (function (verb) {
    return eval(verb)
  })
}
试一下吧:
js> forrest = People()
js> forrest('addMoney')(1000)
js> forrest('restMoney')()
1000
js> forrest('pay')(200)
js> forrest('restMoney')()
800
上篇完。小结一下:消息传递的编程风格指的是,把函数 A 的执行上下文当作对象的数据环境,在此定义对象的动词(函数),然后从此上下文中返回一个可以接受、处理消息的函数(常为匿名)。用函数 A 产生消息处理器作为对象,向此对象传递参数作为消息,以此执行函数 A 环境中定义的动作,这些动作还可能改变所在上下文中用一组数据定义的对象状态。

(To be continue)

  • 氷の鋭

    2007-06-08 17:45:37 氷の鋭 (神不为者,人为之)

    呜呼!我花两个小时写的强文竟然没人顶...我自己顶~

  • huangam

    2007-06-21 15:05:16 huangam

    我顶
    虽然我还没看完

  • Metaphox

    2007-06-21 20:59:38 Metaphox (wie auch immer...)

    角度果然是难以名状,不过我倒觉得你其实不是在教oop,你是在解释一种oop语言的设计理念……

  • 氷の鋭

    2007-06-21 21:16:23 氷の鋭 (神不为者,人为之)

    你说对了... :)
    本来还想写个下的,但是在 JavaEye 上和一群很牛的瘥人掐架很久,身心俱疲。。。
    乘机推荐一下我 JavaEye 上的博客:
    http://lichray.javaeye.com/

  • Metaphox

    2007-06-21 23:10:09 Metaphox (wie auch immer...)

    有个习语叫做language lawyer:

    language lawyer /n./ A person, usually an experienced or senior software engineer, who is intimately familiar with many or most of the numerous restrictions and features (both useful and esoteric) applicable to one or more computer programming languages. A language lawyer is distinguished by the ability to show you the five sentences scattered through a 200-plus-page manual that together imply the answer to your question "if only you had thought to look there".

    拿来描述你很合适。

  • 氷の鋭

    2007-06-22 12:24:41 氷の鋭 (神不为者,人为之)

    我一般会说这个:"If only you had studied the SICP."

  • Metaphox

    2007-06-22 16:24:26 Metaphox (wie auch immer...)

    還是那句話,國內的實用主義氛圍壓倒一切,SICP這種書一般是多數人不知道,知道的多數人也沒看過。其實不只計算機,數學也一樣。從小學到大學都只學解題,數學哲學根本不涉及。結果就是培養出一群只能做外包并且以做外包為榮的程序員,和一群能拿數學奧賽冠軍,卻拿不到Fields獎的人。

    抱歉用繁體,不是我的電腦

  • 氷の鋭

    2007-06-22 19:32:56 氷の鋭 (神不为者,人为之)

    让我想起“信息学”奥赛...那叫一个无聊啊!纯粹是比谁单个算法写的快;想跑的快,把教科书上的东西背下来就行了。。。也许对于比赛来说,这确实很“实用”。。。

  • bobo

    2007-06-25 15:35:24 bobo (穷理,正心,修己)

    别说拿图灵奖了,就是John Carmack这种bt的程序员也出不了.

  • 月亮上的石头

    2007-08-15 21:05:22 月亮上的石头

    不错的文章。

    希望楼主不要专注于掐架。

  • veking

    2007-08-16 09:12:27 veking

    very very ... good!!!

    ------------------------------
    最近怎么没有更新了啊,要有毅力哦,
    坚持下去,不管你有没有时间做这件
    事情。
    记住:坚持下去做一件事情,是非常了不起的。。。
    千万不要虎头蛇尾!!!

  • 氷の鋭

    2007-08-16 15:13:58 氷の鋭 (神不为者,人为之)

    我很有毅力啊,很有毅力地背单词,考托福,学SAT,未来还在我手中。

  • veking

    2007-08-16 22:27:39 veking

    呵呵,有毅力就好。

    学这么多英语是不是准备要出国啊?

  • Handy

    2007-12-04 03:05:19 Handy (谁说蓝色等于忧伤看天空和海洋)

     (function (verb) {
            eval(verb)();
          }
    嘿嘿 喜欢这种用法 可是eval用多了的效率不知咋样

  • Handy

    2007-12-04 14:36:32 Handy (谁说蓝色等于忧伤看天空和海洋)

    顺便问下 我可以转载吗?

  • vincent

    2008-01-11 17:55:31 vincent

    哈,lz把sicp第三章换了样子解说啊

  • 氷の鋭

    2008-01-11 19:44:47 氷の鋭 (神不为者,人为之)

    对了,差不多,扩展在(下)中。
    可以转载,但这篇文章来自的博客是我一个有版权的博客,见于:
    http://pkblogs.com/let-in
    参见 Copyleft 声明。

  • banroo

    2009-01-03 16:23:30 banroo

    top


这个小组的成员也喜欢去   · · · · · · 

上海趣事
上海趣事 (29)
扯淡er
扯淡er (470)
完美web
完美web (759)
WEB标准
WEB标准 (2837)
CSharp
CSharp (24)
AJAX
AJAX (1747)