后面的算法描述是这样的。

    1. 得到当前数组的this对象
    2. 求出当前数组的length属性
    3. 如果报错就返回
    4. 如果 map 方法的参数callbackfn不可执行,就报错
    5. 如果 map 方法的参数之中,指定了this,就让T等于该参数,否则Tundefined
    6. 生成一个新的数组A,跟当前数组的length属性保持一致
    7. 如果报错就返回
    8. 设定k等于 0
    9. 只要k小于当前数组的length属性,就重复下面步骤
      1. 设定Pk等于ToString(k),即将K转为字符串
      2. 设定kPresent等于HasProperty(O, Pk),即求当前数组有没有指定属性
      3. 如果报错就返回
      4. 如果kPresent等于true,则进行下面步骤
        1. 设定kValue等于Get(O, Pk),取出当前数组的指定属性
        2. 如果报错就返回
        3. 设定mappedValue等于Call(callbackfn, T, «kValue, k, O»),即执行回调函数
        4. 如果报错就返回
        5. 设定status等于CreateDataPropertyOrThrow (A, Pk, mappedValue),即将回调函数的值放入A数组的指定位置
        6. 如果报错就返回
      5. k增加 1
    10. 返回

    仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步中第 2 步时,kPresent会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。

    V8 引擎对map方法的如下,可以看到跟规格的算法描述完全一致。

    1. function ArrayMap(f, receiver) {
    2. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
    3. // loop will not affect the looping and side effects are visible.
    4. var array = TO_OBJECT(this);
    5. var length = TO_LENGTH_OR_UINT32(array.length);
    6. return InnerArrayMap(f, receiver, array, length);
    7. }
    8. function InnerArrayMap(f, receiver, array, length) {
    9. if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
    10. var accumulator = new InternalArray(length);
    11. var is_array = IS_ARRAY(array);
    12. var stepping = DEBUG_IS_STEPPING(f);
    13. for (var i = 0; i < length; i++) {
    14. if (HAS_INDEX(array, i, is_array)) {
    15. var element = array[i];
    16. // Prepare break slots for debugger step in.
    17. if (stepping) %DebugPrepareStepInIfStepping(f);
    18. accumulator[i] = %_Call(f, receiver, element, i, array);
    19. }
    20. }
    21. var result = new GlobalArray();
    22. %MoveArrayContents(accumulator, result);