木匣子

Web/Game/Programming/Life etc.

Array.map() 与 parseInt()

最近经常看到黑 Javascript 的人引用这样一个例子:

["1", "2", "3"].map(parseInt);
// 预期 [1, 2, 3]
// 结果 [1, NaN, NaN]

由于预期不一样,然后他们就各种炮轰 js。实际上这是一个误用,而这些人往往不愿深究其因,看客也把它当成一个笑柄,以讹转讹。但是只要用点心,就会发现其中的问题。

让我们先看看规范是如何定义 Array.map() 和 parseInt() 的吧:

Array.map()

MDN 给出了一个 Javascript 版的兼容方案,其返回结果与 native 相同,但方便我们理解 Array.map() 的工作机制。

map 是在最近的ECMA-262 标准中新添加的方法;所以一些旧版本的浏览器可能没有实现该方法. 在那些没有原生支持map 方法的浏览器中.你可以使用下面的javascript代码来实现它.所使用的算法正是 ECMA-262,第5版规定的. 假定Object, TypeError, 和 Array 有他们的原始值.而且callback.call的原始值也是 Function.prototype.call

// 实现 ECMA-262, Edition 5, 15.4.4.19
// 参考: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
  Array.prototype.map = function(callback, thisArg) {

    var T, A, k;

    if (this == null) {
      throw new TypeError(" this is null or not defined");
    }

    // 1. 将O赋值为调用map方法的数组.
    var O = Object(this);

    // 2.将len赋值为数组O的长度.
    var len = O.length >>> 0;

    // 4.如果callback不是函数,则抛出TypeError异常.
    if ({}.toString.call(callback) != "[object Function]") {
      throw new TypeError(callback + " is not a function");
    }

    // 5. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
    if (thisArg) {
      T = thisArg;
    }

    // 6. 创建新数组A,长度为原数组O长度len
    A = new Array(len);

    // 7. 将k赋值为0
    k = 0;

    // 8. 当 k < len 时,执行循环.
    while(k < len) {

      var kValue, mappedValue;

      //遍历O,k为原数组索引
      if (k in O) {

        //kValue为索引k对应的值.
        kValue = O[ k ];

        // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
        mappedValue = callback.call(T, kValue, k, O);

        // 返回值添加到新书组A中.
        A[ k ] = mappedValue;
      }
      // k自增1
      k++;
    }

    // 9. 返回新数组A
    return A;
  };      
}

注意到 mappedValue = callback.call(T, kValue, k, O); // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.

实现上 callback 得到了三个参数,也就是说,如果使用 parseInt 作为回调函数的话,实际上是这样执行的:parseInt(kValue, k, O) 。这会出现什么问题呢?如果 parseInt 只使用一个参数的话,完全没有问题,但不巧的是,实际上 parseInt 接受两个参数(很多人不知道)。接下来我们看看 parseInt 的用法。

parseInt()

parseInt 以指定的底数将一个字符串解析为整数。

语法

parseInt(string, radix);

MDN 的文档建议无论何时都不要省略 radix 以免给读者带来困惑。很多开发者常常把 radix 省去,并希望 parseInt 能按他们的想法工作。实际上如果省略了 radix 会有很多意想不到的结果,它并不总是以10进制为底工作。有兴趣的话可以仔细读读这篇文档


如何避免这种杯具呢?MDN 的中文页面有人补充了一个正确使用 Array.map() 和 parseInt() 的例子:

function returnInt(element){
  return parseInt(element, 10);
}

["1", "2", "3"].map(returnInt);
// 返回[1,2,3]

###参考资料