[].forEach.call() 在 JavaScript 中做了什么?
What does [].forEach.call() do in JavaScript?
我正在查看一些代码片段,我发现多个元素在节点列表上调用函数,其中forEach应用于空数组。
例如,我有这样的东西:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
但我不明白它是如何工作的。谁能解释一下 forEach 前面的空数组的行为以及call
是如何工作的?
[]
是一个数组。
根本不使用此数组。
它被放在页面上,因为使用数组可以访问数组原型,例如.forEach
.
这比打字Array.prototype.forEach.call(...);
接下来,forEach
是一个将函数作为输入的函数......
[1,2,3].forEach(function (num) { console.log(num); });
。对于 this
中的每个元素(其中 this
类似于数组,因为它有一个length
,您可以像 this[1]
一样访问它的各个部分(,它将传递三件事:
- 数组中的元素 元素
- 的索引(第三个元素将传递
2
( - 对数组的引用
最后,.call
是函数具有的原型(它是一个在其他函数上调用的函数(。
.call
将采用它的第一个参数,并将常规函数内部的this
替换为你传递call
的任何参数,作为第一个参数(undefined
或null
将在日常JS中使用window
,或者将是你传递的任何参数,如果在"严格模式"下(。其余参数将传递给原始函数。
[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"
因此,您正在创建一种调用 forEach
函数的快速方法,并且this
从空数组更改为所有<a>
标记的列表,并且对于按顺序的每个<a>
,您正在调用提供的函数。
编辑
逻辑结论/清理
下面,有一篇文章的链接,建议我们放弃函数式编程的尝试,每次都坚持手动的内联循环,因为这个解决方案是黑客式的和难看的。
我想说的是,虽然.forEach
不如它的对应物有用,.map(transformer)
、.filter(predicate)
、.reduce(combiner, initialValue)
,但当你真正想做的只是修改外部世界(不是数组(时,它仍然可以使用 n 次,同时可以访问arr[i]
或i
。
那么我们如何处理这种差异,因为Motto显然是一个有才华和知识渊博的人,我想想象我知道我在做什么/我要去哪里(时不时......其他时候是头脑优先的学习(?
答案其实很简单,由于疏忽,鲍勃叔叔和克罗克福德爵士都会面对:
清理它。
function toArray (arrLike) { // or asArray(), or array(), or *whatever*
return [].slice.call(arrLike);
}
var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
现在,如果你质疑你是否需要这样做,你自己,答案很可能是否定的......
这确切的事情是由...如今,每个(?(库都具有高阶功能。
如果你使用 lodash 或 underscore,甚至是 jQuery,它们都会有一种方式来获取一组元素,并执行 n 次操作。
如果你没有使用这样的东西,那么一定要写你自己的。
lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end);
lib.extend = function (subject) {
var others = lib.array(arguments, 1);
return others.reduce(appendKeys, subject);
};
ES6(ES2015( 及更高版本的更新
slice( )
/array( )
/etc 帮助程序方法不仅会让想要像使用数组一样使用列表的人(理应如此(更轻松,而且对于那些可以奢侈地在相对不久的将来的 ES6+ 浏览器中操作或今天在 Babel 中"转译"的人来说,您内置了语言功能, 这使得这种事情变得没有必要。
function countArgs (...allArgs) {
return allArgs.length;
}
function logArgs (...allArgs) {
return allArgs.forEach(arg => console.log(arg));
}
function extend (subject, ...others) { /* return ... */ }
var nodeArray = [ ...nodeList1, ...nodeList2 ];
超级干净,非常有用。
查找">其余"和"展开"运算符;在 BabelJS 网站上试用它们;如果您的技术堆栈井井有条,请在生产中使用它们与 Babel 和构建步骤。
没有充分的理由不能使用从非数组到数组的转换......只是不要把你的代码弄得一团糟,只是在任何地方粘贴同样丑陋的行。
querySelectorAll
方法返回一个 NodeList
,它类似于数组,但它不完全是一个数组。因此,它没有forEach
方法(数组对象通过 Array.prototype
继承(。
由于NodeList
类似于数组,数组方法实际上将对其进行处理,因此通过使用[].forEach.call
,您可以在NodeList
的上下文中调用Array.prototype.forEach
方法,就好像您已经能够简单地执行yourNodeList.forEach(/*...*/)
一样。
请注意,空数组文字只是扩展版本的快捷方式,您可能也会经常看到:
Array.prototype.forEach.call(/*...*/);
其他答案已经很好地解释了这段代码,所以我只添加一个建议。
这是一个很好的代码示例,为了简单明了,应该对其进行重构。不要每次都使用 [].forEach.call()
或 Array.prototype.forEach.call()
,而是用它创建一个简单的函数:
function forEach( list, callback ) {
Array.prototype.forEach.call( list, callback );
}
现在你可以调用这个函数,而不是更复杂和晦涩的代码:
forEach( document.querySelectorAll('a'), function( el ) {
// whatever with the current node
});
可以使用更好地编写
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {
});
is 的作用是document.querySelectorAll('a')
返回一个类似于数组的对象,但它不继承自 Array
类型。因此,我们从 Array.prototype
对象调用 forEach
方法,上下文作为document.querySelectorAll('a')
返回的值
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
它基本上与:
var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
// whatever with the current node
});
想更新这个老问题:
在现代浏览器中使用 [].foreach.call()
循环访问元素的原因基本上已经结束。我们可以直接使用document.querySelectorAll("a").foreach()
。
NodeList 对象是节点的集合,通常由 属性(如 Node.childNodes(和方法(如 document.querySelectorAll((.
虽然 NodeList 不是一个数组,但可以迭代它 with forEach((.它也可以使用转换为真正的数组 Array.from((.
但是,一些较旧的浏览器尚未实现 NodeList.forEach(( 也不是 Array.from((。这可以通过使用 Array.prototype.forEach(( — 请参阅本文档的示例。
这个页面上有很多好的信息(见答案+答案+评论(,但我最近遇到了与OP相同的问题,需要一些挖掘才能获得整个画面。所以,这里有一个简短的版本:
目标是在本身没有这些方法的类似数组NodeList
上使用Array
方法。
较旧的模式通过Function.call()
选择了Array的方法,并使用数组文字([]
(而不是Array.prototype
,因为它的键入更短:
[].forEach.call(document.querySelectorAll('a'), a => {})
一个较新的模式(在 ECMAScript 2015 之后(是使用 Array.from()
Array.from(document.querySelectorAll('a')).forEach(a => {})
空数组在其原型中具有属性forEach
,该属性是一个函数对象。(空数组只是获取对所有Array
对象都具有的forEach
函数的引用的简单方法。反过来,函数对象具有一个 call
属性,该属性也是一个函数。当您调用函数的call
函数时,它会使用给定的参数运行该函数。第一个参数在被调用的函数中变为this
。
您可以在此处找到 call
函数的文档。有关forEach
的文档,请点击此处。
只需添加一行:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
瞧!
document.querySelectorAll('a').forEach(function(el) {
// whatever with the current node
});
享受 :—(
警告:NodeList 是一个全局类。 如果您编写公共库,请不要使用此建议。但是,当您在网站或节点.js应用程序上工作时,这是提高自我效能的非常方便的方法。
只是一个快速而肮脏的解决方案,我总是最终使用。我不会碰原型,就像好的做法一样。当然,有很多方法可以让它变得更好,但你明白了。
const forEach = (array, callback) => {
if (!array || !array.length || !callback) return
for (var i = 0; i < array.length; i++) {
callback(array[i], i);
}
}
forEach(document.querySelectorAll('.a-class'), (item, index) => {
console.log(`Item: ${item}, index: ${index}`);
});
[]
总是返回一个新数组,它等效于 new Array()
但保证返回一个数组,因为Array
可以被用户覆盖,而[]
不能。所以这是获取Array
原型的安全方法,然后如上所述,call
用于在类似数组的节点列表(this(上执行函数。
调用具有给定此值和提供的参数的函数 单独。中药
假设你有: const myList= document.querySelectorAll("p");
这将返回 HTML 中所有的列表/数组
。现在Array.prototype.forEach.call(myList, myCallback)
相当于[].forEach.call(myList, myCallback)
其中"myCallback"是一个回调函数。你基本上是在myList的每个元素上运行回调函数。希望这对你有帮助!
Norguard解释了[].forEach.call()
做什么,James Allardice解释了我们为什么这样做:因为querySelectorAll返回了一个没有forEach方法的NodeList
...
除非您拥有Chrome 51 +,Firefox 50 +,Opera 38,Safari 10等现代浏览器。
如果没有,您可以添加 Polyfill:
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = function (callback, thisArg) {
thisArg = thisArg || window;
for (var i = 0; i < this.length; i++) {
callback.call(thisArg, this[i], i, this);
}
};
}
是否有任何限制,但它有效。我使用 spread 运算符将 nodeList 转换为迭代器对象并对其进行映射:
let _btns = document.querySelectorAll('.btn');
[..._btns].map(function(elem, i) {
elem.addEventListener('click', function (e) {
console.log(elem.textContent);
})
})
.btn {
padding: 5px;
color:#fff;
background-color: darkred;
text-align:center;
color: white;
}
<button class="btn">button 1</button>
<button class="btn">button 2</button>
- 这是什么 ==- javascript 运算符
- 使用较少代码隐藏和显示选择菜单内容的更好方法是什么?javascript
- 我可以使用什么Javascript或Jquery库来处理带有IE8的GANTT图表
- 这是什么JavaScript语法
- 并行开发同步/阻塞和异步/非阻塞库api的好方法是什么?(JavaScript)
- 我用什么javascript解析器在node的javascript中重新实现jsCoverage
- 两个代码之间的区别是什么(javascript的循环)
- 在循环中定义函数的最佳方法是什么?- JavaScript
- 存在什么 JavaScript 库,允许部分加载较大的图像
- 可以使用什么 Javascript 正则表达式模式来捕获可能嵌套括号中的文本
- 我如何知道在运行 V8 时编译了什么 JavaScript
- 这是什么javascript命令
- 我如何使用Firebug来告诉我正在激发什么JavaScript
- 如何处理这个琐事游戏的答案I'我在做什么?(Javascript)
- 我的代码中缺少什么?Javascript和XML
- duojs使用什么javascript模块api
- 我使用什么JavaScript模式从原型方法访问私有方法
- 确定在chrome中调用什么javascript函数
- 在一个函数中,返回另一个函数的目的是什么?(Javascript)
- 看看当你按下一个按钮时使用了什么(javascript)函数