谁能解释一下这个Array.prototype.find()polyfill?

时间:2014-06-12 11:22:17

标签: javascript arrays

此MDN页面上的

[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find] 有这个polyfill:

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}

我的问题是以下几行:

var list = Object(this);
var length = list.length >>> 0;

因为this绝对是一个数组(我们正在扩充Array.prototype)所以为什么要确保length是数字,为什么使用Object()?

1 个答案:

答案 0 :(得分:11)

根本的答案是,polyfill只是忠实地实现了ES6草案规范中的算法的第1步和第4步(截至5月22日草案的第22.1.3.8节)。

  

1。设 O 是调用ToObject传递 this 值作为参数的结果。

     

...

     

4。让 len 为ToLength( lenValue )。

(ToLength基本上是转换为数字。)

因为现在可以使用this的非对象值(通过Function#callFunction#apply,或者只是在严格模式下直接调用该函数),步骤1才有意义

  

因为this绝对是一个数组(我们正在扩充Array.prototype)所以为什么要确保长度是数字,为什么使用Object()

但我们知道this是一个数组。请参阅当前ES6草案规范中的这一说明:

  

find函数是故意通用的;它不要求其this值为Array对象。因此,它可以转移到其他类型的对象以用作方法。 find函数是否可以成功应用于非Array的异常对象是依赖于实现的。

您将在几乎所有分配给原型的预定义函数中找到该注释。

例如,您可以将它们用于其他事情,或者直接将它们分配给对象或其他原型:

MyNiftyThing.prototype.find = Array.prototype.find;

...或通过Function#callFunction#apply

所以假设我想在Array#find对象上使用arguments。当然,arguments array-like ,但不是数组。所以我可能会这样做:

function foo() {
    var firstObject = Array.prototype.find.call(arguments, function(val) {
        return typeof val === "object";
    });
    // ...
}

一个更着名的例子(不涉及find)正在使用slice将类似数组的对象转换为数组:

function foo() {
    // Turn the `arguments` pseudo-array into a real array
    var args = Array.prototype.slice.call(arguments, 0);
}