javascript中的三个地图实现。哪一个更好?

时间:2009-10-07 21:06:34

标签: javascript map

我为某项任务编写了一个简单的地图实现。然后,出于好奇,我又写了两篇。我喜欢map1,但代码有点难以阅读。如果有人感兴趣,我会很感激简单的代码审查。

哪一个更好?你知道在javascript中实现这个的其他方法吗?

var map = function(arr, func) {
  var newarr = [];
  for (var i = 0; i < arr.length; i++) {
    newarr[i] = func(arr[i]);
  }
  return newarr;
};

var map1 = function(arr, func) {
  if (arr.length === 0) return [];
  return [func(arr[0])].concat(funcmap(arr.slice(1), func));
};

var map2 = function(arr, func) {
  var iter = function(result, i) {
    if (i === arr.length) return result;
    result.push(func(arr[i]));
    return iter(result, i+1);
  };
  return iter([], 0);
};

谢谢!

修改

我正在考虑这种功能。

例如,现在我将使用它来迭代这样:

map(['class1', 'class2', 'class3'], function(cls) { 
    el.removeClass(cls);
});

ids = map(elements, extract_id); 
/* elements is a collection of html elements, 
   extract_id is a func that extracts id from innerHTML */

6 个答案:

答案 0 :(得分:5)

如果在Firefox和SpiderMonkey上本地使用的map实现怎么样,我认为这是非常直接的:

if (!Array.prototype.map) {
  Array.prototype.map = function(fun /*, thisp*/)   {
    var len = this.length >>> 0;  // make sure length is a positive number
    if (typeof fun != "function") // make sure the first argument is a function
      throw new TypeError();

    var res = new Array(len);  // initialize the resulting array
    var thisp = arguments[1];  // an optional 'context' argument
    for (var i = 0; i < len; i++) {
      if (i in this)
        res[i] = fun.call(thisp, this[i], i, this);  // fill the resulting array
    }

    return res;
  };
}

如果您不想扩展Array.prototype,请将其声明为正常的函数表达式。

答案 1 :(得分:3)

作为参考,map在jQuery中实现如下

map: function( elems, callback ) {
    var ret = [];

    // Go through the array, translating each of the items to their
    // new value (or values).
    for ( var i = 0, length = elems.length; i < length; i++ ) {
        var value = callback( elems[ i ], i );

        if ( value != null )
            ret[ ret.length ] = value;
    }

    return ret.concat.apply( [], ret );
}

这似乎与您的第一个实现最相似。我会说第一个是首选,因为它是最简单的阅读和理解。但如果您关注性能,请对其进行分析。

答案 2 :(得分:2)

我认为这取决于当func可能更改数组时你想要map做什么。我倾向于在简单性和样本长度方面犯错一次。

您始终可以在

中指定输出尺寸
var map = function(arr, func) {
  var n = arr.length & 0x7fffffff;  // Make sure n is a non-neg integer
  var newarr = new Array(n);  // Preallocate array size
  var USELESS = {};
  for (var i = 0; i < n; ++i) {
    newarr[i] = func.call(USELESS, arr[i]);
  }
  return newarr;
};

我使用了func.call()形式而不仅仅是func(...),因为我不喜欢调用用户提供的代码而没有指定'this'是什么,而是YMMV。

答案 3 :(得分:2)

第一个是最合适的。对于每个数组项递归一个级别在函数式语言中可能有意义,但是在没有尾调用优化的过程语言中,它是疯狂的。

但是,map上已经有Array函数:它由ECMA-262第五版定义,作为内置函数,它将是最佳选择。使用:

alert([1,2,3].map(function(n) { return n+3; })); // 4,5,6

唯一的问题是所有当前浏览器都不支持第五版:特别是IE中不存在数组扩展。但你可以通过对Array原型进行一些补救工作来解决这个问题:

if (!Array.prototype.map) {
    Array.prototype.map= function(fn, that) {
        var result= new Array(this.length);
        for (var i= 0; i<this.length; i++)
            if (i in this)
                result[i]= fn.call(that, this[i], i, this);
        return result;
    };
}

根据ECMA标准,此版本允许在函数调用中传入可选对象以绑定到this,并跳过任何缺失值(在JavaScript中合法,以获得长度列表3,没有第二项)。

答案 4 :(得分:1)

第二种方法有问题。 'funcmap'不应该更改为'map1'?

如果是这样 - 这个方法失败了,因为concat()方法很昂贵 - 从给定的方法创建新数组,所以必须分配额外的内存并在O(array1.length + array2.length)中执行。

我最喜欢你的第一个实现 - 它绝对是最容易理解的,并且似乎很快就可以执行了。没有额外的声明(比如第三种方式),额外的函数调用 - 只有一个用于循环和array.length赋值。

答案 5 :(得分:0)

我会说第一个赢得简单(并立即可理解);性能将高度依赖于手头的引擎优化,因此您必须在您想要支持的引擎中进行分析。