ES6规格之数组的 map 方法

数组的 map 方法

规格的22.1.3.15 小节定义了数组的map方法。该小节先是总体描述map方法的行为,里面没有提到数组空位。

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

  1. LetObeToObject(this value).
  2. ReturnIfAbrupt(O).
  3. LetlenbeToLength(Get(O, "length")).
  4. ReturnIfAbrupt(len).
  5. IfIsCallable(callbackfn)isfalse, throw a TypeError exception.
  6. IfthisArgwas supplied, letTbethisArg; else letTbeundefined.
  7. LetAbeArraySpeciesCreate(O, len).
  8. ReturnIfAbrupt(A).
  9. Letkbe 0.
  10. Repeat, whilek<len\ a. LetPkbeToString(k).\ b. LetkPresentbeHasProperty(O, Pk).\ c.ReturnIfAbrupt(kPresent).\ d. IfkPresentistrue, then\ d-1. LetkValuebeGet(O, Pk).\ d-2.ReturnIfAbrupt(kValue).\ d-3. LetmappedValuebeCall(callbackfn, T, «kValue, k, O»).\ d-4.ReturnIfAbrupt(mappedValue).\ d-5. LetstatusbeCreateDataPropertyOrThrow (A, Pk, mappedValue).\ d-6.ReturnIfAbrupt(status).\ e. Increasekby 1.
  11. ReturnA.

翻译如下。

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

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

 
  1. const arr = [, , ,];
  2. arr.map(n => {
  3. console.log(n);
  4. return 1;
  5. }) // [, , ,]

上面代码中,arr是一个全是空位的数组,map方法遍历成员时,发现是空位,就直接跳过,不会进入回调函数。因此,回调函数里面的console.log语句根本不会执行,整个map方法返回一个全是空位的新数组。

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

 
  1. function ArrayMap(f, receiver) {
  2. CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
  3. // Pull out the length so that modifications to the length in the
  4. // loop will not affect the looping and side effects are visible.
  5. var array = TO_OBJECT(this);
  6. var length = TO_LENGTH_OR_UINT32(array.length);
  7. return InnerArrayMap(f, receiver, array, length);
  8. }
  9. function InnerArrayMap(f, receiver, array, length) {
  10. if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
  11. var accumulator = new InternalArray(length);
  12. var is_array = IS_ARRAY(array);
  13. var stepping = DEBUG_IS_STEPPING(f);
  14. for (var i = 0; i < length; i++) {
  15. if (HAS_INDEX(array, i, is_array)) {
  16. var element = array[i];
  17. // Prepare break slots for debugger step in.
  18. if (stepping) %DebugPrepareStepInIfStepping(f);
  19. accumulator[i] = %_Call(f, receiver, element, i, array);
  20. }
  21. }
  22. var result = new GlobalArray();
  23. %MoveArrayContents(accumulator, result);
  24. return result;
  25. }
©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页