我在这里看到 Array.prototype.forEach()
的polyfill,我对它的实现有一个问题:
/*1*/ if (!Array.prototype.forEach)
/*2*/ {
/*3*/ Array.prototype.forEach = function(fun /*, thisArg */)
/*4*/ {
/*5*/ "use strict";
/*6*/
/*7*/ if (this === void 0 || this === null)
/*8*/ throw new TypeError();
/*9*/
/*10*/ var t = Object(this);
/*11*/ var len = t.length >>> 0;
/*12*/ if (typeof fun !== "function")
/*13*/ throw new TypeError();
/*14*/
/*15*/ var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
/*16*/ for (var i = 0; i < len; i++)
/*17*/ {
/*18*/ if (i in t)
/*19*/ fun.call(thisArg, t[i], i, t);
/*20*/ }
/*21*/ };
/*22*/ }
看第10行:为什么他们使用Object(this)
?
当我搜索它的用法时,我看到了这个:
Object构造函数为给定的值创建一个对象包装器。如果值为null或undefined,它将创建并返回一个空值对象,否则,它将返回对应类型的对象到给定的值。如果值已经是一个对象,它将返回价值。
所以他们想检查一下是不是null
|| undefined
。
好的,但是他们已经在#7-#8行检查过了!
问题:
他们使用Object(this)
的原因是什么(我错过了)?
所以他们想检查它是否为null || undefined
。目的是
Object构造函数为给定的值创建一个对象包装器。如果值为空或未定义,它将创建并返回一个空值对象,否则,它将返回类型为对应给定值。如果值已经是一个对象,它将返回值
将原语转换为对象,如"foo"
转换为new String("foo")
。
严格来说,这是没有必要的,因为.length
的属性访问和索引无论如何都会将原语转换为对象。但是,当使用第三个回调参数时,可能会有一个对象(并且每次调用都是相同的对象)。
他们使用
Object(this)
的原因是什么?
主要是为了重现forEach
规范的第1步,即在参数上调用ToObject
。在规范中,这需要能够在其上使用[[Get]]
和[[HasProperty]]
内部方法。
同样,通过只在获得速度时将原语转换为对象
我正在搜索这个问题,因为我不知道自己为什么会这样。目前的正确答案并没有让我百分之百满意。所以我试图执行代码,如果我删除对象(this),只是做var O = this;forEach函数的定义在这里:ECMAScript forEach。所以你可以看到为什么要创建变量和它们的名字等等。polyfil试图尽可能精确。
由于OP只想知道对象(this),所以我将重点关注它。我找到了需要这个的原因。试试下面的代码:
Array.prototype.forEach.call("abc", func);
基本上是循环遍历abc字符串。对象(this)使abc变为:
String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
如果我们不这样做,abc将只是普通的"abc"。然后在填充循环中我们这样做:
kValue = O[k];
在"abc"字符串的情况下,这将获得字符串的每个字母。但这将不起作用,并将在IE <8上显示未定义。
为什么这不起作用,在这句话中解释了其他问题:这个符号在IE7中不起作用。第一个代码片段将在IE7中返回undefined。如果你碰巧在你的代码中对字符串都使用括号符号,并且你想要迁移到。charat (pos),这是一个真正的痛苦:括号在你的代码中被使用,并且没有简单的方法来检测它是用于字符串还是数组/对象。"(源)
但是当使用Object(this)时,这就没有问题了。
另一个我觉得很方便的引语:
" forEach函数故意是泛型的;它不要求它的this值是一个Array对象。因此,它可以作为一种方法转移到其他类型的对象中使用。forEach函数能否成功应用于宿主对象取决于实现。(源)
目的不是检查未定义。相反,它是处理原语。
例如,如果this
是一个数字,例如42
,那么它就是Object(42)
,它是一个值为42
的Number
对象。
这意味着从那时起你可以像对待对象一样对待输入,即使它最初是一个原语。
要测试,请尝试在字符串上使用polyfill -字符串具有.length
属性。例如,尝试…
[].forEach.call("Hello!",function(letter) {console.log(letter);});