代码改变世界

Underscore骨骼

2011-07-02 15:49 by BlueDream, ... 阅读, ... 评论, 收藏, 编辑

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

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

<!DOCTYPE html>
<html>
<head>
<title> Underscore骨骼 </title>
</head>
<body>
<script type="text/javascript">
// 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 === nullreturn;
        
        
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 == nullreturn 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 == nullreturn 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();

alert(re);
</script>
</body> 

< /html>