转载

利用apply和arguments复用方法

首先,有个单例对象,它上面挂了很多静态工具方法。其中有一个是each,用来遍历数组或对象。

var nativeForEach = [].forEach
var nativeMap = [].map
var util = {
    each: function (obj, iterator, context) {
        if (obj == null) return
        if (nativeForEach && obj.forEach === nativeForEach) {
          obj.forEach(iterator, context)
        } else if ( obj.length === +obj.length ) {
            for (var i = 0; i < obj.length; i++) {
                if (iterator.call(obj[i] || context, obj[i], i, obj) === true) return
            }
        } else {
            for (var k in obj) {
                if (iterator.call(obj[k] || context, obj[k], k, obj) === true) return
            }
        }
    },
    map: function(obj, iterator, context) {
        var results = []
        if (obj == null) return results
        if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context)     
        this.each(obj, function(val, i, coll) {
            results[i] = iterator.call(context, val, i, coll)
        })
        return results
    }
}

  

还有诸如every、some等对集合(Array,Hash)操作的工具函数。使用时采用util.xx方式。

 

如果定义了一个集合类,这个类内部有集合数据。

function Collection(data) {
    this.data = data || []
    // some other property
    // this.xxx = yyy
}
Collection.prototype = {
    // some method
}

 

可以很方便的把util上的方法拷贝到集合类上,如

function copyMethod(clazz, obj) {
    for (var method in obj) {
        clazz.prototype[method] = function() {
            var args = [].slice.call(arguments)
            var target = this.data
            args.unshift(target)
            obj[method].apply(obj, args)
        }
    }
}
copyMethod(Collection, util)

 

这样拷贝后,Collection的实例就有了util上的方法,util操作的集合对象(第一个参数)就是Collection的this.data。如下直接可以遍历this.data了。

var coll = new Collection([10, 20, 30]) 

// 遍历
coll.each(function(k) {
    console.log(k)
})

// 操作
var arr = coll.map(function(k) {
   return k - 5
})
console.log(arr) // 5, 15, 25

 

这种模式在很多开源库中使用,比如jQuery,它的 $.each/$.map 很方便的拷贝到了 $().each/$().map。

 

又如Backbone,它的 _.each/_.map/_.every/_.chain (还有很多)都拷贝到了 Collection的原型上。

  // Underscore methods that we want to implement on the Collection.
  // 90% of the core usefulness of Backbone Collections is actually implemented
  // right here:
  var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
    'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
    'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
    'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
    'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
    'lastIndexOf', 'isEmpty', 'chain'];

  // Mix in each Underscore method as a proxy to `Collection#models`.
  _.each(methods, function(method) {
    Collection.prototype[method] = function() {
      var args = slice.call(arguments);
      args.unshift(this.models);
      return _[method].apply(_, args);
    };
  });

 

又有,把 _.keys / _.values / _.pairs / _.invert / _.pick 等对对象操作的实用方法拷贝了 Backbone.Model上 (1.0新增)

var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];

// Mix in each Underscore method as a proxy to `Model#attributes`.
_.each(modelMethods, function(method) {
  Model.prototype[method] = function() {
    var args = slice.call(arguments);
    args.unshift(this.attributes);
    return _[method].apply(_, args);
  };
});

  

正文到此结束
本文目录