ES6...扩展运算符的实现原理-记录TypeError("Invalid attempt to spread non-iterable instance")的问题分析
最近重构的新项目上线,观察前端监控日志有类型错误
TypeError("Invalid attempt to spread non-iterable instance")
提示的报错机型是,Android5.1.1的vivoXplay5,vivo 6sPlus等vivo系列。找不到对应机型但是有Android5.1.1的OPPO,结果OPPO是正常的。只好自己爬代码分析。
该类型错误是因为使用了ES6的扩展运算符展开MapIterator对象报错。但查阅了canisue网站,Map.values等是支持5.1.1系统的。怀疑代码报错的地方是
[...MapObje.values()]
转义后的代码如下
k = Object(o["a"])(u["a"].values())
Object(o["a"])的最终调用如下的d(t)函数
"75fc": function(t, e, n) {
"use strict";
var r = n("a745")
, i = n.n(r);
function o(t) {
if (i()(t)) {
for (var e = 0, n = new Array(t.length); e < t.length; e++)
n[e] = t[e];
return n
}
}
......
function l(t) {
if (u()(Object(t)) || "[object Arguments]" === Object.prototype.toString.call(t))
return s()(t)
}
function f() {
throw new TypeError("Invalid attempt to spread non-iterable instance")
}
function d(t) {
return o(t) || l(t) || f()
}
n.d(e, "a", (function () {
return d
}
))
},
进入d(t)的o(t)中,o(t)的实现就是判断入参是否是数组,也就是Array.isArray(),判断t如果是数组的话直接生成一个新数组,拷贝t中的属性。
function o(t) {
if (i()(t)) {
for (var e = 0, n = new Array(t.length); e < t.length; e++)
n[e] = t[e];
return n
}
}
如果o(t)不满足的话,进入l(t)中,l(t)的u()(Object(t)主要是判断是否实现了Symbol.iterator迭代器接口
t.exports = n("584a").isIterable = function(t) {
var e = Object(t);
return void 0 !== e[i] || "@@iterator"in e || o.hasOwnProperty(r(e))
}
其中 e[i]是指的对象是否有实现Symbol.iterator,否则的话判断是否有@@iterator,或者o.hasOwnProperty(r(e)),
截屏2021-08-29 上午9.34.16.png其中r函数也很简单,是一个类型检测功能
void 0 === t ? "Undefined" : null === t ? "Null" : "string" == typeof (n = a(e = Object(t), i)) ? n : o ? r(e) : "Object" == (s = r(e)) && "function" == typeof e.callee ? "Arguments" : s
基于以上,猜测目前是因为Android5.1.1的vivo系统可能对Map的Iterator接口实现有问题,但是目前找不到该手机,只能是猜测,后面找到真机后在调试定位。
总结扩展运算符的实现原理:
- 是否是数组,数组直接浅拷贝属性
- 是否实现Iterator接口,检测方式包括是否有实现Symbol.iterator、@@Iterator、是否是其他可迭代对象等(叫可迭代也不合适好像)、或者对象本身是否是Arguments
- 都不满足的话抛出类型错误。
网友评论