JavaScript嵌套函数for循环调用导致选择排序结果异常
问题表现
使用JavaScript实现选择排序时运行结果不符合预期:
- 预期
sortArray()返回由6个"5"组成的数组[5,5,5,5,5,5] - 实际运行仅返回包含单个值5的数组
- 测试发现注释掉代码行
smallest_index = find_smallest(nums)后,即可得到预期输出
原始实现代码如下:
let nums = [2, 1, 3, 4, 5, 6]; function sortArray(nums) { let arr = new Array(); let smallest_index; for (i = 0; i < nums.length; i++) { smallest_index = find_smallest(nums); arr.push("5"); } return arr; } function find_smallest(arr) { let smallest = arr[0]; let smallest_index = 0; for (i = 1; i < arr.length; i++) { if (arr[i] < smallest) { console.log("this"); smallest = arr[i]; smallest_index = i; } } return smallest_index; } console.log(sortArray(nums));
问题根因
核心错误是循环变量未声明导致全局作用域污染:
- 两个函数内的for循环都没有用
let/const声明循环变量i,直接对i赋值的写法在JavaScript中会把i注册为全局作用域的共享变量,两个函数操作的是同一个i - 第一次进入
sortArray的循环时,全局i初始值为0,随即调用find_smallest;find_smallest内部的循环会从i=1一直遍历到i等于数组长度(即6)才退出 - 从
find_smallest返回sortArray的循环判断逻辑时,全局i的值已经是6,判断i < nums.length(6 < 6)结果为false,循环直接终止,仅执行了1次push操作,因此最终数组只有1个元素。这也是注释掉find_smallest调用后,循环能正常执行6次得到预期结果的原因。
额外说明:当前代码还未实现选择排序的完整逻辑,即使修复了循环变量问题,每次调用
find_smallest拿到的都是原数组第一个最小值的索引,没有把已找到的最小值从原数组移除,也无法完成正确的排序。
修复方案
- 所有for循环的循环变量统一用
let声明,做块级作用域隔离,彻底避免变量跨函数污染 - 如果要实现完整选择排序逻辑,需要在每次找到最小值索引后,将对应元素从待排序数组中删除,再推入结果数组,保证下一次查找能找到剩余元素里的最小值
修复后的可运行代码:
let nums = [2, 1, 3, 4, 5, 6]; function sortArray(nums) { // 浅拷贝入参,避免修改外部原数组 let sourceArr = [...nums]; let sortedArr = new Array(); let smallest_index; // 声明块级作用域循环变量 for (let i = 0; i < nums.length; i++) { smallest_index = find_smallest(sourceArr); // 移除已找到的最小值,推入结果数组 sortedArr.push(sourceArr.splice(smallest_index, 1)[0]); } return sortedArr; } function find_smallest(arr) { let smallest = arr[0]; let smallest_index = 0; // 声明块级作用域循环变量 for (let i = 1; i < arr.length; i++) { if (arr[i] < smallest) { smallest = arr[i]; smallest_index = i; } } return smallest_index; } console.log(sortArray(nums)); // 正确排序输出 [1,2,3,4,5,6]
如果只是要实现测试阶段返回6个"5"的效果,只需要给两个循环的i加上let声明即可,不需要补全排序逻辑。
内容的提问来源于stack exchange,提问作者Exodus Reed
相关产品推荐
相关产品推荐

