首页 > 代码库 > Underscore骨骼

Underscore骨骼

转一篇underscore的文章给大家

underscore函数大全以及例子http://www.tuicool.com/articles/ZjAfqe 

上次写过一篇QWrap骨骼的文章,个人认为,想要了解一个库或框架,首先从他的核心思想入手,理解其思想,那么剩余的就仅仅是方法的堆砌。近年比较火的一个jsMVC框架backbone他的核心强依赖库为underscore。

抽空提取了一下他的骨骼,其实主要就是他的链式操作的实现,别个剩下的就是具体函数的实现了,当然对于Underscore函数式也是它的最大亮点,可以好好看下函数的实现。

// Underscore骨骼
(function () {
    var root = this;
    
    
    var breaker = {};

    var ArrayProto = Array.prototype, ObjProto = Object.prototype;

    var nativeForEach    = ArrayProto.forEach,
        nativeMap        = ArrayProto.map,
        nativeFilter    = ArrayProto.filter,
        nativeKeys        = Object.keys;

    var slice            = ArrayProto.slice,
        unshift            = ArrayProto.unshift,
        hasOwnProperty  = ObjProto.hasOwnProperty;

    // 构造函数
    var _ = function (obj) { return new wrapper(obj); };

    // 向全局暴露接口
    root._ = _;

    // 类型检测
    _.isNumber = function (obj) {
        return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
    };
    _.isFunction = function (obj) {
        return !!(obj && obj.constructor && obj.call && obj.apply);
    };

    // 遍历扩展
    var each = _.each = _.forEach = function (obj, iterator, context) {
        if (obj === null) return;
        
        if (nativeForEach && obj.forEach === nativeForEach) {
            obj.forEach(iterator, context);
        } else if (_.isNumber(obj.length)) {
            for (var i = 0, l = obj.length; i < l; i++) {
                if (iterator.call(context, obj[i], i, obj) === breaker) return; 
            }
        } else {
            for (var key in obj) {
                if (hasOwnProperty.call(obj, key)) {
                    if (iterator.call(context, obj[key], key, obj) === breaker) return; 
                } 
            }
        }
    };

    // 返回对象上面的函数名
    _.functions = _.methods = function (obj) {
        return _.filter(_.keys(obj), function (key) { return _.isFunction(obj[key])}).sort();
    };

    // 过滤数组
    _.filter = _.select = function (obj, iterator, context) {
        var results = [];
        if (obj == null) return results; 
        if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
        
        each(obj, function (value, index, list) {
            if (iterator.call(context, value, index, list)) results[results.length] = value; 
        });
        return results;
    };

    // 获取key值
    _.keys = nativeKeys || function (obj) {
        if (obj !== Object(obj)) throw new TypeError(‘Invalid object‘);
        var keys = [];
        for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
        return keys;
    };

    // 用于实验的map方法
    _.map = function (obj, iterator, context) {
        var results = [];
        if (obj == null) return results;
        if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
        each(obj, function (value, index, list) {
            results[results.length] = iterator.call(context, value, index, list);
        });
        return results;
    };

    // 链式操作主要部分
    var wrapper = function (obj) { this._wrapped = obj; };

    _.prototype = wrapper.prototype;

    // 扩展自定义方法到Wrap包装器上
    _.mixin = function (obj) {
        each(_.functions(obj), function (name) {
            addToWrapper(name, _[name] = obj[name]);
        });
    };
    
    // 是否对结果进行链式包装返回
    var result = function (obj, chain) {
        return chain ? _(obj).chain() : obj;
    };

    // 将方法扩展到包装器的原型上
    var addToWrapper = function (name, func) {
        wrapper.prototype[name] = function () {
            var args = slice.call(arguments);
            unshift.call(args, this._wrapped);
            return result(func.apply(_, args), this._chain);
        };
    };

    // 将所有Underscore上的方法添加到Wrap包装器上
    _.mixin(_);

    // 扩展Array上的方法到wrap包装器上-包装原数组
    each([‘pop‘, ‘push‘, ‘reverse‘, ‘shift‘, ‘sort‘, ‘splice‘, ‘unshift‘], function (name) {
        var method = ArrayProto[name];
        wrapper.prototype[name] = function () {
            method.apply(this._wrapped, arguments);
            return result(this._wrapped, this._chain);
        };
    });
    
    // 扩展Array上的方法到wrap包装器上-包装返回值
    each([‘concat‘, ‘join‘, ‘slice‘], function (name) {
        var method = ArrayProto[name];
        wrapper.prototype[name] = function () {
            return result(method.apply(this._wrapped, arguments), this._chain);
        };
    });


    // 添加链式方法的实现
    wrapper.prototype.chain = function () {
        this._chain = true;
        return this;
    };

    // 提取链式包装的内容
    wrapper.prototype.value = function () {
        return this._wrapped;
    };

})();

// 结果测试 步骤:将数组[1,2,3]进行链式包装,然后将其用val * 2来map,紧接着用filter进行val < 5过滤, 
// 然后pop,concat最后获取其值value
var re = _([1,2,3]).chain().map(function (val) {
    return val * 2;
}).filter(function (val) {
    return val < 5;
}).pop().concat([‘5‘]).value();


Underscore骨骼