zoukankan      html  css  js  c++  java
  • OOP 诡异教程(下)

    这是最终确定的 JavaScript 基于消息传递编程风格的文章“OOP 诡异教程(上)”的下篇。原文地址:http://let-in.blogspot.com/2007/06/oop.html。原来的想法是以风格开头,谈到 JavaScript 的内部机制,但作者 lichray 迟迟没有动键盘,认为不如利用已有的风格做一套机制出来,这样可能更有意义。于是,就有了这个更加“诡异”的下篇。

    四. 扩展的实现
    上文最后给出了一个“看上去很美”的基于消息传递的编程风格,比如构造一个 People 类的代码类似:

    function People () {
      var money = 0
      function setMoney (dollars) {
        money = dollars
      }
      function pay (dollars) {
        money -= dollars
      }
      return (function (verb) {
        return eval(verb)
      })
    }

    有了这样的语法我们就可以描述不少句子了。但是存在一个问题:现实中的 Objects 之间是存在关系的——比如,forrest 是个 IQ 为 75 的傻子,傻子是 People 的一种。而我们仅仅是生搬硬套了一种语法而割裂了这种 "is-a" 关系。现在我们的工作,目的之一就是让这样一个“真切”的世界从我们已有的编程风格的地基上拔地而起。
    到底应该怎样做才能使 Fool 产生的对象都能响应 People 的消息呢?我们要给 Fool 产生的对象(也就是返回的那个匿名函数啦)都添加这样一种能力:如果在 Fool 中响应不了消息,那就反馈给 People 响应。

    function Fool (iq) {
      var IQ = iq || 0
      function init (iq) {
        IQ = iq
      }
      return (function (verb) {
        try {
          return eval(verb)
        } catch (e) {
          return People()(verb)
        }
      })
    }

    js> forrest = Fool()
    js> forrest('init')(75)
    js> forrest('IQ')
    75
    js> forrest('money')
    0

    五. 语法扩展和代码生成
    这下代码量增加了很多,强迫潜在的使用者们在创建每个类时都这样写那实在是令人抓狂。本来这篇文章应该不提此类问题的解决,但考虑到有益于读者理解“机制”这个抽象概念,这里给出一个可行的方案——把普通的类代码用 Function() 函数重编译为可用的 JavaScript 函数。也就是说,我们能给出类扩展的代码并指定被扩展的类来获取类似上文的代码:

    Fool = extend('People()', function (iq){
      var IQ = iq || 0
      function init (iq) {
        IQ = iq
      }
    })

    为了方便字符串操作,我们希望编译后的代码的参数部分(如 People())都集中出现在一个位置且尽可能便于定位。在函数头添加一句

    var origin = People()

    当然是可行的,这样还能使 Fool 内部显式引用到其超类。但这样还不够漂亮。我们修改编译后的样例代码为:

    function () {
      return (function (origin) {
        var IQ = 0
        function init (iq) {
          IQ = iq
        }
        return (function (verb) {
          try {
            return eval(verb)
          } catch (e) {
            return origin(verb)
          }
        })
      })(People())
    }

    这个利用参数传递变量的小技巧不值得学习,实际效率不高。但在这篇文章中,这样绑定特殊变量的技术是标准方案。
    那么,extend() 函数的实现为:

    function extend (originc, code) {
      function argsArea (code) {
        // 题外话,正则表达式也有不值得使用的时候
        return code.slice(code.indexOf('(')+1, code.indexOf(')'))
      }
      function bodyCode (code) {
        // 不用 trim() 了,没事儿找事儿
        return code.slice(code.indexOf('{')+1, code.lastIndexOf('}'))
      }
      function format (body) {
        var objc = bodyCode(function () {
          return (function (verb) {
            try {
              return eval(verb)
            } catch (e) {
            return origin(verb)
            }
          })
        }.toString())
        return 'return (function (origin) {'+body+objc+'})('+originc+')'
      }
      var $ = code.toString()
      return Function(argsArea($), format(bodyCode($)))
    }

    这样前文提到过的 extend 的实例代码就可以正常运行了,测试代码不再重复。

    六. 机制完备化
    这样,我们的基于消息传递编程风格的一套面向对象机制就确定下来了。机制是宪法,是语言的根本大法,有了它,我们就可以通过修改代码生成器,很快地给这套机制进行完备化。
    想法有很多,例子只举两个。
    第一个例子:类的定义中应该能直接引用到将产生的对象 self。答案只有一句话:把返回的那个作为对象的匿名函数命名为 self。
    第二个例子:既然是单继承模式,应当存在一个顶层类 AbsObj,使没有指定继承的类自动继承它。答案也只有一句话:在 extend 函数体第一行添加代码:

    if (arguments.length == 1) {
      code = originc
      originc = 'AbsObj()'
    }

    然后手工构造设计 AbsObj 类,为空也无所谓。不过当然了,一般都会给顶层类添加一些全局性质的消息绑定。由于是“底层操作”,基本上都需要修改 extend 函数。做了一个简单的:

    function AbsObj () {
      //检测是否能响应此 verb,要再用一次异常处理
      function canHandle(verb){
        try {
          // 别担心这里的 self 会传递不过去
          self(verb)
        } catch (e) {
          return false
        }
        return true
      }
      function toString() {} // 这个搞起来其实很麻烦~`
      var self = function (verb) {
        return eval(verb)
      }
      return self
    }

    js> Obj=extend(function(){x=5})
    js> o=Obj()
    js> o('canHandle')('x')
    true
    js> o('canHandle')('y')
    false

    文章写完了,小结一下。消息传递的编程不仅仅是一种代码风格,还可以成长为一种完备的机制。这种完备性远不只是这两篇加起来不到300行的文章所能覆盖的(例如非常彻底的“万物皆对象”,因为只要是能响应消息的函数,连接一下 AbsObj 就是合法对象了;类,函数都可以),大家可以试着玩一玩,顺便体会一下这个计算模型的透明和强大。
    另外,熟悉函数式编程的朋友可以帮忙思考一下:这样一个基于闭包变换的计算模型实质上是函数式的,再配合动态的函数式的对象级继承(用一个匿名类代换一下)就能在纯 FP 真正下实现 OOP 了。可惜的是每一次更新操作都要重新生成对象,性能代价大了点,不知道大家有什么好想法。
  • 相关阅读:
    排序算法最强总结及其代码实现
    程序员面试常问的小算法总结
    Docker部署Scrapy-redis分布式爬虫框架(整合Selenium+Headless Chrome网页渲染)
    使用亚马逊AWS云服务器进行深度学习——免环境配置/GPU支持/Keras/TensorFlow/OpenCV
    阿里巴巴MySQL开源中间件Canal入门
    【秒杀系统】秒杀系统实战(四)| 缓存与数据库双写一致性实战
    【秒杀系统】秒杀系统实战(五)| 如何优雅的完成订单异步处理
    把购买数据添加到购物车
    实现自动登陆和记住用户名功能
    自定义登陆标签
  • 原文地址:https://www.cnblogs.com/4kapple/p/1312351.html
Copyright © 2011-2022 走看看