在Internet Explorer上,Array.from的替代方案或填充方案是什么?

43
我在Internet Explorer上遇到了Angular应用程序的问题。它在其他浏览器上都没有问题(Chrome,Mozilla,Edge),但是在IE上却有问题。
我已经使用开发人员工具定位了错误,它返回了错误出现在以下行:
myDataSet[index - 1].data = Array.from(tmp);
这是我收到的错误消息:
“对象不支持属性或方法'from'”
在那里我正在使用一个名为“tmp”的Set(),其中包含以下数据:

enter image description here

然后,我从这个 Set 中创建了一个简单的数组对象。

我该如何解决这个问题?

编辑

基于建议,我已经在我的应用程序中添加了以下代码:

if (!Array.from) {
  Array.from = (function () {
    var toStr = Object.prototype.toString;
    var isCallable = function (fn) {
      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
    };
    var toInteger = function (value) {
      var number = Number(value);
      if (isNaN(number)) { return 0; }
      if (number === 0 || !isFinite(number)) { return number; }
      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
    };
    var maxSafeInteger = Math.pow(2, 53) - 1;
    var toLength = function (value) {
      var len = toInteger(value);
      return Math.min(Math.max(len, 0), maxSafeInteger);
    };

    // The length property of the from method is 1.
    return function from(arrayLike/*, mapFn, thisArg */) {
      // 1. Let C be the this value.
      var C = this;

      // 2. Let items be ToObject(arrayLike).
      var items = Object(arrayLike);

      // 3. ReturnIfAbrupt(items).
      if (arrayLike == null) {
        throw new TypeError("Array.from requires an array-like object - not null or undefined");
      }

      // 4. If mapfn is undefined, then let mapping be false.
      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
      var T;
      if (typeof mapFn !== 'undefined') {
        // 5. else
        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
        if (!isCallable(mapFn)) {
          throw new TypeError('Array.from: when provided, the second argument must be a function');
        }

        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
        if (arguments.length > 2) {
          T = arguments[2];
        }
      }

      // 10. Let lenValue be Get(items, "length").
      // 11. Let len be ToLength(lenValue).
      var len = toLength(items.length);

      // 13. If IsConstructor(C) is true, then
      // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
      // 14. a. Else, Let A be ArrayCreate(len).
      var A = isCallable(C) ? Object(new C(len)) : new Array(len);

      // 16. Let k be 0.
      var k = 0;
      // 17. Repeat, while k < len… (also steps a - h)
      var kValue;
      while (k < len) {
        kValue = items[k];
        if (mapFn) {
          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
          A[k] = kValue;
        }
        k += 1;
      }
      // 18. Let putStatus be Put(A, "length", len, true).
      A.length = len;
      // 20. Return A.
      return A;
    };
  }());
}
6个回答

58

Array.from不支持以下文档模式:Quirks,Internet Explorer 6标准,Internet Explorer 7标准,Internet Explorer 8标准,Internet Explorer 9标准,Internet Explorer 10标准,Internet Explorer 11标准。在Windows 8.1中不支持(兼容性参考)。

只需将以下代码添加到您的页面中(JS代码摘自developer.mozilla.org),它将模拟ES6的Array.from方法。

Array.from被添加到ECMA-262标准的第6版中;因此,它可能不存在于标准的其他实现中。您可以通过在脚本开头插入以下代码来解决此问题,在不本地支持它的实现中使用Array.from。这个算法恰好是在ECMA-262的第6版中指定的算法,假设Object和TypeError具有它们的原始值,并且callback.call评估为Function.prototype.call的原始值。此外,由于真正的可迭代对象不能进行polyfill,因此此实现不支持ECMA-262第6版中定义的通用可迭代对象。

if (!Array.from) {
  Array.from = (function () {
    var toStr = Object.prototype.toString;
    var isCallable = function (fn) {
      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
    };
    var toInteger = function (value) {
      var number = Number(value);
      if (isNaN(number)) { return 0; }
      if (number === 0 || !isFinite(number)) { return number; }
      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
    };
    var maxSafeInteger = Math.pow(2, 53) - 1;
    var toLength = function (value) {
      var len = toInteger(value);
      return Math.min(Math.max(len, 0), maxSafeInteger);
    };

    // The length property of the from method is 1.
    return function from(arrayLike/*, mapFn, thisArg */) {
      // 1. Let C be the this value.
      var C = this;

      // 2. Let items be ToObject(arrayLike).
      var items = Object(arrayLike);

      // 3. ReturnIfAbrupt(items).
      if (arrayLike == null) {
        throw new TypeError("Array.from requires an array-like object - not null or undefined");
      }

      // 4. If mapfn is undefined, then let mapping be false.
      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
      var T;
      if (typeof mapFn !== 'undefined') {
        // 5. else
        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
        if (!isCallable(mapFn)) {
          throw new TypeError('Array.from: when provided, the second argument must be a function');
        }

        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
        if (arguments.length > 2) {
          T = arguments[2];
        }
      }

      // 10. Let lenValue be Get(items, "length").
      // 11. Let len be ToLength(lenValue).
      var len = toLength(items.length);

      // 13. If IsConstructor(C) is true, then
      // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
      // 14. a. Else, Let A be ArrayCreate(len).
      var A = isCallable(C) ? Object(new C(len)) : new Array(len);

      // 16. Let k be 0.
      var k = 0;
      // 17. Repeat, while k < len… (also steps a - h)
      var kValue;
      while (k < len) {
        kValue = items[k];
        if (mapFn) {
          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
          A[k] = kValue;
        }
        k += 1;
      }
      // 18. Let putStatus be Put(A, "length", len, true).
      A.length = len;
      // 20. Return A.
      return A;
    };
  }());
}

1
谢谢您的解释。请问,您能建议我使用什么代码来替换我的 Array.from 吗? - Robert J.
我已经更新了我的回答。只需将 JavaScript 代码复制到您的页面中,例如主 JS 文件中,您的问题就会得到解决! - Ali Mamedov
1
你可以尝试使用 myDataSet[index - 1].data = Array.prototype.slice.call(tmp); - Redu
2
这根本没有解决在IE中的问题。 - Richard Zilahi
1
在添加了您的代码并在 Internet Explorer 11 中进行检查后,我遇到了错误。该网站使用不支持可迭代对象的实现来覆盖 Array.from() 方法,这可能会导致 Google Maps JavaScript API v3 无法正确工作。 - Kamlesh
显示剩余3条评论

15

我遇到了同样的问题。查看了Polyfill,它很庞大。这里有一个两行短的解决方案。

原帖的作者基本上需要从他的类数组对象中创建一个简单的数组。我使用了最有效率的两行普通 for 循环(我必须将HTML DOM节点列表转换为数组,对JavaScript arguments 对象也适用)。

对于原帖的作者而言,可以这样来解决:

var temp_array = [],
    length = tmp.length;

for (var i = 0; i < length; i++) {
    temp_array.push(tmp[i]);
}

// Here you get the normal array "temp_array" containing all items
// from your `tmp` Set.

将其分为独立的函数,您将获得一个适用于IE<9的通用可重复使用的3行解决方案。

下面是独立函数的示例:

/**
 * @param arr The array | array-like data structure.
 * @param callback The function to process each element in the 'arr'.
 * The callback signature and usage is assumed similar to the 
 * native JS 'forEach' callback argument usage.
 */
function customEach(arr, callback) {
    'use strict';
    var l = arr.length;
    for (var i = 0; i < l; i++) {
        callback(arr[i], i, arr);
    }
};

提示:在forEach回调函数中查看如何使用customEach回调。


7
虽然IE不支持,但您可以使用 MDN 的polyfill

7
你可以使用 slice.call 处理类数组对象。这意味着你的代码将会是这样的: myDataSet[index - 1].data = [].slice.call(tmp);

一行代码完美运行,甚至在IE中也能正常工作。 - fakie

4

正确的答案和MDN文档的第一篇答案

问题的原始发布者写道:

我正在做的是,我有一个名为tmpSet()...

但是我们已经有了3个答案超过4年了,没有人使用Set对象测试它。
来自MDN的庞大polyfill无法与Set对象一起使用!

这段代码被从MDN复制并粘贴到接受的答案中而没有进行测试。

我的polyfill解决方案也比MDN中的不正确且巨大的polyfill更短。

在以下解决方案中,您将在注释中找到有关函数及其参数的说明。

/**
 * @param "arr" (required) - array-like or iterable object to convert it to an array.
 * @param "callbackFn" (optional) - function to call on every element of the array.
 * @param "thisArg" (optional) - value to use as this when executing callback
 * Return value - new Array instance
 *
 * The callbackFn argument usage is like in Array.map() callback.
 * The callbackFn function accepts the following arguments:
 *      @param "currentValue" (required) - the current element being processed in the array.
 *      @param "index" (optional) - the index of the current element being processed in the array.
 *      @param "array" (optional) - he array map was called upon.
 * Callback function that is called for every element of "arr". Each time callback executes, the returned value is added to new array ("arNew").
 */
function arrayFrom(arr, callbackFn, thisArg)
{
    //if you need you can uncomment the following line
    //if(!arr || typeof arr == 'function')throw new Error('This function requires an array-like object - not null, undefined or a function');

    var arNew = [],
        k = [], // used for convert Set to an Array
        i = 0;

    //if you do not need a Set object support then
    //you can comment or delete the following if statement
    if(window.Set && arr instanceof Set)
    {
        //we use forEach from Set object
        arr.forEach(function(v){k.push(v)});
        arr = k
    }

    for(; i < arr.length; i++)
        arNew[i] = callbackFn
            ? callbackFn.call(thisArg, arr[i], i, arr)
            : arr[i];

    return arNew
}

//You could also use it without the following line, but it is not recommended because native function is faster.
Array.from = Array.from || arrayFrom; //We set it as polyfill

//HOW TO USE IT:

function myCallback1(x){return x+x}

function myCallback2(o){return o.innerHTML}

var str = 'Super!',
    array = str.split(''),//['S','u','p','e','r','!']
    arrayLike1 = window.Set ? new Set(str) : array, //array for IE < 10. Only 11 version of IE supports Set.
    arrayLike2 = document.querySelectorAll('b');//NodeList
    arrayLike3 = document.getElementsByTagName('b');//HTMLCollection

console.log(arrayFrom(str).join(','));//S,u,p,e,r,!
console.log(arrayFrom(array).join(','));//S,u,p,e,r,!
console.log(arrayFrom(str, myCallback1).join(','));//SS,uu,pp,ee,rr,!!
console.log(arrayFrom(arrayLike1, myCallback1).join(','));//SS,uu,pp,ee,rr,!!
console.log(arrayFrom(arrayLike2, myCallback2).join(','));//aaa,bbb
console.log(arrayFrom(arrayLike3, myCallback2).join(','));//aaa,bbb
//You can also use it as polyfill:
console.log(Array.from(str).join(','));//S,u,p,e,r,!
<b>aaa</b> <b>bbb</b>

不要忘记,Set(集合)中的值是唯一的!例如:

//Only 11 version of IE and all modern browsers support Set.
var ar = [];
new Set('mama').forEach(function(v){ar.push(v)});
console.log(ar.join(',')); // 'm','a'


1

虽然它不是完整的 polyfill,但以下内容到目前为止已经适用于我所有的使用情况:

Array.from||(Array.from=function(ar){return Array.prototype.slice.call(ar)});

我几年前在MDN上发现了这个内容,但它们已经删除了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接