如何为JavaScript变量赋值函数及数组元素函数访问报错排查
解决JavaScript函数赋值与数组元素访问的问题
首先咱们先搞定你遇到的报错B[i].value is not a function——这是因为你把B[i]直接赋值成了function value(),也就是说B[i]本身就是这个函数,而不是一个带有value方法的对象。所以调用的时候应该直接用B[i](),而不是B[i].value()。
不过这只是表面问题,就算改了调用方式,你会发现所有输出都是undefined,这是因为闭包的变量捕获陷阱:循环里用var声明的i是函数级作用域的变量,所有创建的函数都会共享同一个i的引用。当循环结束后,i的值已经变成了A.length(也就是3),所以所有函数返回的都是A[3],自然是undefined。
下面给你几种正确的解决方案:
方案1:使用let声明循环变量(ES6+)
let在循环中会为每次迭代创建一个独立的块级作用域,每个函数都会捕获当前迭代的i值:
function test(A) { var B = new Array(); for(let i=0; i<A.length; i++) { // 这里用let代替var B[i] = function value() { return A[i]; }; } for(let i=0; i< B.length; i++) { console.log(B[i]()); // 直接调用函数,不是.value() } return B; } A=[1,2,3]; B = test(A);
方案2:使用立即执行函数表达式(IIFE)创建局部作用域
兼容ES5及更早版本,通过IIFE把当前的i值传递给函数:
function test(A) { var B = new Array(); for(var i=0; i<A.length; i++) { B[i] = (function(currentIndex) { // 用IIFE包裹,传入当前i return function value() { return A[currentIndex]; }; })(i); } for(var i=0; i< B.length; i++) { console.log(B[i]()); // 直接调用函数 } return B; } A=[1,2,3]; B = test(A);
方案3:使用Array.prototype.forEach遍历数组
forEach的回调函数会为每个元素创建独立的作用域,避免闭包陷阱:
function test(A) { var B = new Array(); A.forEach(function(item, index) { B[index] = function value() { return item; // 直接捕获当前item,或者也可以用index取A[index] }; }); B.forEach(function(func) { console.log(func()); // 调用函数 }); return B; } A=[1,2,3]; B = test(A);
额外说明:如果确实需要B[i].value()的调用方式
如果你希望B[i]是一个对象,带有value方法,那可以调整赋值逻辑,把B[i]变成包含方法的对象:
function test(A) { var B = new Array(); for(let i=0; i<A.length; i++) { B[i] = { value: function() { // 把value作为对象的方法 return A[i]; } }; } for(let i=0; i< B.length; i++) { console.log(B[i].value()); // 现在这样调用就没问题了 } return B; } A=[1,2,3]; B = test(A);
这种写法就完全符合你最初要求的A[i] === B[i].value()的逻辑啦。
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

