为什么.forEach()不在Array.prototype.fillB()中工作? (自定义功能)

时间:2015-04-12 06:51:53

标签: javascript prototype

我开始尝试原型,然后遇到了一个我不理解的案例。

我正在尝试将一个.fill(val)方法添加到Array原型中。

此代码取自另一个StackOverflow答案:

Array.prototype.fillA = function(val) {
  var i;

  for (i = 0; i < this.length; i++) {
    this[i] = val;
  }

  return this;
};

看起来我可以用.forEach清理它:

Array.prototype.fillB = function(val) {

  this.forEach(function(origVal, i, theArray) {
    theArray[i] = val;
  });

  return this;
};

但这不起作用。 (调用.fillB后,数组尚未更改。)

那么,.fillB究竟出了什么问题?

以下是一些演示此问题的Runnable Snippets:

.fillA()

Array.prototype.fillA = function(val) {
  var i;

  for (i = 0; i < this.length; i++) {
    this[i] = val;
  }

  return this;
};

var arr,
  output;

arr = new Array(5).fillA(5);

alert('arr = "' + arr.join(',') + '"');

.fillB()

Array.prototype.fillB = function(val) {

  this.forEach(function(origVal, i, theArray) {
    theArray[i] = val;
  });

  return this;
};

var arr,
  output;

arr = new Array(5).fillB(5);

alert('arr = "' + arr.join(',') + '"');

免责声明:

  • 我知道你应该在覆盖之前检查原型中是否存在.fill()函数。
  • 像这样修改一个全局原型可能会导致其他代码库处理意外的后果,例如(for..in等),所以这通常是一个坏主意。
  • .fillA和.fillB实际上会做不同的事情,即.fillA只能在从0开始的连续数组上正常工作,但.fillB应该适用于所有数组。

2 个答案:

答案 0 :(得分:4)

原因记录为here

  

forEach()按升序为数组中的每个元素执行一次提供的回调。对于已删除或未定义的索引,不会调用它,因为在初始化数组时没有为它们提供任何值。

new Array(5)创建一个长度为5的数组(所以fillA()按预期工作),但没有值。

答案 1 :(得分:1)

如果您创建一个类似Array(number)的数组,那么您创建的数组只有length属性,并且没有任何元素。

forEachmap等为每个实际存在的元素执行函数,忽略length属性。

使用Array(number)创建的数组实际上是空的。

如果将几个参数传递给Array,它将创建一个包含这些参数作为元素的数组。

因此,如果您使用Array.apply(0, Array.call(0, 5))代替Array(5),则会创建一个包含5个undefined元素的数组。

工作原理.apply使用数组作为参数,并使用length属性检查大小。 所以这一行首先用Array.call(0, 5)创建一个空数组(你可以使用Array(5)),然后使用length属性查找0,1,2,3,4,5元素,因为它们从未定义过,所以每个值都是undefined

换句话说,执行Array.apply(0, Array.call(0, 5))与执行Array(undefined, undefined, undefined, undefined, undefined)相同。

&#13;
&#13;
Array.prototype.fillB = function(val) {

  this.forEach(function(origVal, i, theArray) {
    theArray[i] = val;
  });

  return this;
};

var arr,
  output;

arr = Array.apply(0, Array.call(0, 5)).fillB(5);

alert('arr = "' + arr.join(',') + '"');
&#13;
&#13;
&#13;

同样,你可以写Array.apply(0, Array(5))。 (我不是,因为jslint抱怨这种语法)