zoukankan      html  css  js  c++  java
  • 《JavaScript语言精粹》之函数化

    写在前面

    看到好多书评和读书笔记都说《JavaScript语言精粹》字字珠玑,名不虚传。。当然,要看得懂才行

    其实个人认为函数化部分不是很好,举的例子不是十分恰当,之前看不懂是因为被成功误导了,就像《Head First》设计模式第一章《策略模式》一样,作者有些偏离章节主题,读者容易被误导

    声明:姑且把函数化部分给出的用来创建对象的函数称为“创造函数”吧,为了与“构造函数”区分开。。不是很好听,将就着用吧

    一.源码中需要注意的点

    很容易就能拿到源码,和中文版书上的代码一样,仔细看了一遍发现了一个很精妙的地方,当然,不是很好理解

    P.S.源码有点小问题:“创造函数”cat花括号不匹配,中文版54页,return that;之前少了};

    Object的原型函数superior内部有一个很精妙的地方

    Object.method('superior', function (name) {
        var that = this,
            method = that[name];
        return function (  ) {
            return method.apply(that, arguments);
        };
    });

    亮点就是最后一句的arguments,看似无心,其实是有意为之的,表明用superior调用父类方法时也可以传参。当然,传参的话需要修改调用方式,测试代码如下:

    Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
    };
    
    var mammal = function (spec) {
        var that = {};
    
        that.get_name = function (  ) {
            return spec.name;
        };
    
        that.says = function (  ) {
            return spec.saying || '';
        };
    
        return that;
    };
    
    var cat = function (spec) {
        spec.saying = spec.saying || 'meow';
        var that = mammal(spec);
        that.purr = function (n) {
            var i, s = '';
            for (i = 0; i < n; i += 1) {
                if (s) {
                    s += '-';
                }
                s += 'r';
            }
            return s;
        };
        that.get_name = function (  ) {
            alert("cat.get_name :" + arguments.length);///
            return that.says(  ) + ' ' + spec.name +
                    ' ' + that.says(  ) + "[" + arguments.length + "]";
        };
    
        return that;
    };
    
    Object.method('superior', function (name) {
        var that = this,
            method = that[name];
        return function (  ) {
            alert("superior :" + arguments.length);///
            return method.apply(that, arguments);
        };
    });
    
    var coolcat = function (spec) {
        var that = cat(spec),
            super_get_name = that.superior('get_name');
        that.get_name = function () {
            alert("coolcat.get_name :" + arguments.length);///
            return 'like ' + super_get_name.apply(this, arguments) + ' baby';
        };
        return that;
    };
    
    var myCoolCat = coolcat({name: 'Bix'});
    var name = myCoolCat.get_name(1, 2, 3);
    //        'like meow Bix meow baby'
    
    
    alert(name);    // 'like meow Bix meow[3] baby'

    P.S.开始以为superior函数最后的arguments是作者的错误,觉得应该需要把外面的arguments对象传给method而不是里面的,绕了一大圈发现是自己错了,道行不够,没能秒懂道格拉斯大爷的意思。。

    二.函数化的初衷

    函数化部分开篇就说明了初衷:为了实现私有属性,创建最后提到的“防伪对象”

    目的无可厚非,实现私有属性太有必要了。但举的例子mammal -> cat -> coolcat太不合适了,作者想说明用函数化的方式可以实现继承

    当然,不是严格意义上的继承,因为函数化方式没有用到自定义类型,子类实例与父类实例的is-a关系也就无从谈起了

    P.S.看第一遍的时候cat的例子就把我带到沟里去了,以为函数化就是要抛弃new,完全用函数来实现继承。。自然是在沟里越走越深了

    三.函数化的思想

    直接看代码,代码自己会说话:

    /*
     * 函数化的思想:
     * 1.创建对象
     * 2.添加私有属性
     * 3.公开接口(添加公有属性)
     * 4.返回该对象
     */
    /*
     * method: getSuper
     * @param spec 规格说明对象,提供创建对象所需的基本数据
     * @param my “创造函数”之间共享数据的容器
     */
    function getSuper(spec, my){
        var obj;            // 要返回的对象
        var my = my || {};  // 没传入就创建一个
    
        // 私有属性
        var attr = spec.value;  // 从规格说明对象取数据
        var fun = function(){
            alert(attr);
        }
    
        // [可选]把需要与其它“创造函数”共享的数据装入my
    
        // 创建对象,可以用任意方式,比如new、字面量、调用其它“创造函数” 
        obj = {
            name: "SuperObject"
        };
    
        // 公开接口
        obj.fun1 = fun;
    
        // 返回obj
        return obj;
    }
    
    /*
     * method: getSub
     * 参数同上
     */
    function getSub(spec, my){
        var obj;
        var my = my || {};
    
        // 私有属性
        var attr = spec.value + 1;
        var fun = function(){
            alert(attr);
        }
    
        // [可选]共享
    
        // 创建对象
        obj = getSuper(spec, my);   // 可以直接传过去,当然也可以改一改再传,或者传别的什么
    
        // 公开接口
        obj.fun2 = fun;
    
        // 返回obj
        return obj;
    }
    
    // 测试
    var spec = {
        value: 1
    };
    var sub = getSub(spec); // 不用传入my,my只应该在“创造函数”之间用
    sub.fun1(); // 1
    sub.fun2(); // 2

    P.S.又是“创建对象 -> 增强 -> 返回新对象”这个套路,不就是尼古拉斯所说的由道格拉斯发明的“模块模式”吗?

    四.防伪对象(持久性的对象)

    函数化部分的核心就是它了,注意上面例子中公开接口的方式:

    // 私有属性
    var myFun = function(){/* ... */};
    // 公开接口
    obj.fun = myFun;

    而不直接用:

    // 公开接口
    obj.fun = function(){/* ... */};

    第一种方式更安全,因为即便从外界修改了fun,内部其它调用了myFun的方法仍然可以正常工作,这样的函数对象就是所谓的防伪对象

    完整定义:

    防伪对象的属性可以被替换或者删除,但该对象的完整性不会受到损害

    也被称为持久性的对象,一个持久性对象就是一个简单功能函数的集合

    后话

    到这里《JavaScript语言精粹》的学习笔记就告一段落了,补好了[函数化]的空缺,学习笔记的其它部分请查看黯羽轻扬:《JavaScript语言精粹》学习笔记

  • 相关阅读:
    Java (三)APACHE Commons IO 常规操作
    JavaFX FileChooser文件选择器,缓存上一次打开的目录
    JavaFX FileChooser文件选择器、DirectoryChooser目录选择器
    javaFX 在窗口的标题栏显示当前时间,1秒更新一次时间
    Java 实现截屏
    composer安装包的时候触发PHP fatal error,提示允许的内存耗光
    箭头函数
    js中的寄生组合继承
    构造函数的原型
    在string.replace中使用具名组匹配
  • 原文地址:https://www.cnblogs.com/ayqy/p/4487009.html
Copyright © 2011-2022 走看看