• 一步一步学习underscore的封装和扩展方式


    前言

    underscore虽然有点过时,这些年要慢慢被Lodash给淘汰或合并。
    但通过看它的源码,还是能学到一个库的封装和扩展方式。

    第一步,不污染全局环境。

    ES5中的JS作用域是函数作用域。
    函数内部可以直接读取全局变量,当然函数外部无法读取函数内的局部变量。
    所以,我们在匿名函数里啪啪啪写代码,妈妈再也不会担心修改到全局变量。

    (funtion(){
    
        var _ = function(obj) {
            return new wrapper(obj);
        };
    
        var wrapper = function(obj) {
            this._wrapped = obj;
        };
    
    
        window._ = _;
    })()
    

    第二步,扩展实例方法

    首先,我们要知道,
    声明在_.prototype的方法是专门给_实例用。
    声明在wrapper.prototype的方法是给wrapper方法实例用。
    underscore的_方法是一个工厂方法,_方法返回的是私有wrapper方法实例。
    那么如何把_的静态方法赋予给wrapper方法实例?且看以下代码。

    (function(){
    
        var _ = function(obj) {
            return new wrapper(obj);
        };
    
        var wrapper = function(obj) {
            this._wrapped = obj;
        };
    
        var result = function(obj) {
            return obj;
        };
    
    
        var ArrayProto = Array.prototype,
            forEach = ArrayProto.forEach,
            push = ArrayProto.push;
        _.each = forEach;
    
        _.type = function(obj){
            return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
        }
    
        _.isFunction = function(fn){
            return (_.type(fn) == "function");
        }
    
        _.functions = function(obj) {
            var names = [];
            for (var key in obj) {
                if (_.isFunction(obj[key])) names.push(key);
            }
            return names.sort();
        }
    
        _.mixin = function(obj) {
            forEach.call(_.functions(obj), function(name) {
                var func = _[name] = obj[name];
                _.prototype[name] = function() {
                    var args = [this._wrapped];
                    push.apply(args, arguments);
                    return result( func.apply(_, args));
                };
            });
        };
        
        // _.prototype 指向  wrapper.prototype
        _.prototype = wrapper.prototype;
        // 修复_实例的原型链
        _.prototype.constructor = _;
        // 这里通过mixin方法把_的静态方法,赋值给wrapper实例
        _.mixin(_);
        
    
        window._ = _;
    })();
    

    测试代码

    var str = _("str");
    str.type(); //"String"
    str instanceof _; //true
    

    第三步,支持链式调用。

    链式调用使得操作同一个对象时非常方便。
    实现的思路是,重新包装调用的函数,缓存函数调用结果,使其返回的值是wrapper方法实例。

    (function(){
        var _ = function(obj) {
            return new wrapper(obj);
        };
    
        var wrapper = function(obj) {
            this._wrapped = obj;
        };
    
        // 链式包装函数
        var result = function(obj, chain) {
            return chain ? _(obj).chain() : obj;
        };
    
        // 触发可链式函数
        wrapper.prototype.chain = function() {
            // this._chain用来标示当前对象是否使用链式操作
            this._chain = true;
            return this;
        };
    
        // 当触发可链式后,用这个来取结果值
        wrapper.prototype.value = function() {
            return this._wrapped;
        };
        var ArrayProto = Array.prototype,
            forEach = ArrayProto.forEach;
    
        // 这些数组方法需要包装以下才可以链式调用
        forEach.call(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    
            var method = ArrayProto[name];
            wrapper.prototype[name] = function() {
                var wrapped = this._wrapped;
                // 调用Array对应的方法并返回结果
                method.apply(wrapped, arguments);
                var length = wrapped.length;
                if ((name == 'shift' || name == 'splice') && length === 0) {
                    delete wrapped[0];
                }
                return result(wrapped, this._chain);
            };
        });
    
    
        // 这些数组方法本身可链式调用
        forEach.call(['concat', 'join', 'slice'], function(name) {
            var method = ArrayProto[name];
            wrapper.prototype[name] = function() {
                return result(method.apply(this._wrapped, arguments), this._chain);
            };
        });
    
        window._ = _;
    
    })();
    

    测试代码

    var a =_([1, 2]).chain().push(3).push(4).push(5);
    a.value(); // [1, 2, 3, 4, 5]
    [1,2].push(3).push(4).push(5); // Uncaught TypeError: [1,2].push(...).push is not a function(…)
    

    第四步,模块化支持。

    ES6 Modules之前,UMD很盛行,我们要支持。

    (function(){
        var _ = function(obj) {
            return new wrapper(obj);
        };
    
        var wrapper = function(obj) {
            this._wrapped = obj;
        };
    
        if (typeof define === 'function' && define.amd) {
            define('underscore', [], function() {
                return _;
            });
        } else if (typeof exports !== 'undefined') {
            if (typeof module !== 'undefined' && module.exports) {
                exports = module.exports = _;
            }
            exports._ = _;
        } else {
            window['_'] = _;
        }
    )();
    

    第五步,综合代码

    (function() {
    
        var _ = function(obj) {
            return new wrapper(obj);
        };
    
        if (typeof define === 'function' && define.amd) {
            define('underscore', [], function() {
                return _;
            });
        } else if (typeof exports !== 'undefined') {
            if (typeof module !== 'undefined' && module.exports) {
                exports = module.exports = _;
            }
            exports._ = _;
        } else {
            window['_'] = _;
        }
    
        var wrapper = function(obj) {
            this._wrapped = obj;
        };
    
        var result = function(obj, chain) {
            return chain ? _(obj).chain() : obj;
        };
    
        wrapper.prototype.chain = function() {
            this._chain = true;
            return this;
        };
    
        wrapper.prototype.value = function() {
            return this._wrapped;
        };
    
        var ArrayProto = Array.prototype,
            forEach = ArrayProto.forEach,
            push = ArrayProto.push;
        _.each = forEach;
    
        _.type = function(obj){
            return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
        }
    
        _.isFunction = function(fn){
            return (_.type(fn) == "function");
        }
    
        _.functions = function(obj) {
            var names = [];
            for (var key in obj) {
                if (_.isFunction(obj[key])) names.push(key);
            }
            return names.sort();
        }
    
        _.mixin = function(obj) {
            forEach.call(_.functions(obj), function(name) {
                var func = _[name] = obj[name];
                _.prototype[name] = function() {
                    var args = [this._wrapped];
                    push.apply(args, arguments);
                    return result( func.apply(_, args),this._chain);
                };
            });
        };
    
        _.prototype = wrapper.prototype;
        _.prototype.constructor = _;
        _.mixin(_);
    
        forEach.call(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    
            var method = ArrayProto[name];
            wrapper.prototype[name] = function() {
                var wrapped = this._wrapped;
                method.apply(wrapped, arguments);
                var length = wrapped.length;
                if ((name == 'shift' || name == 'splice') && length === 0) {
                    delete wrapped[0];
                }
                return result(wrapped, this._chain);
            };
        });
    
    
        forEach.call(['concat', 'join', 'slice'], function(name) {
            var method = ArrayProto[name];
            wrapper.prototype[name] = function() {
                return result(method.apply(this._wrapped, arguments), this._chain);
            };
        });
    
    
    
    })();
    
  • 相关阅读:
    live2d 快速实现好看的看板娘特效
    JQuery 日期转换日期方法封装
    SQL Server 之 DateTime的常用方法
    C# 之DateDiff 时间差扩展方法
    SQL Server 之如何查询某数据库下的触发器和语句
    Css 设置固定表格头部,内容可滚动
    jquery 点击tr选中checkbox,解决checkbox的默认点击事件被阻止的问题
    VS切换代码自动补全模式
    C#实现软键盘的几个关键技术介绍
    C# 模拟软件键盘输入,使Winfrom窗体不获取鼠标焦点方法
  • 原文地址:https://www.cnblogs.com/samwu/p/5619029.html
Copyright © 2020-2023  润新知