无法跨JS文件调用导出的game对象的LeftOrRightRow方法
问题排查结论
你的导入导出语法本身完全正确,已经实现了导出整个game对象的需求,不需要调整导出/导入的写法。调用失败是两个JavaScript基础疏漏导致的:
- 核心原因:ES Module的模块作用域隔离导致
squares变量未定义。每个JS模块文件有独立的私有作用域,你在2048.js的LeftOrRightRow方法里直接使用的squares变量,是main.js里DOM加载完成后才声明的DOM元素集合,2048.js的作用域根本访问不到这个变量,运行时会直接抛出squares is not defined的引用错误。这也正好解释了为什么函数和调用逻辑写在同一个文件时能正常运行——同文件下代码共享同一个作用域,变量可以直接访问。 - 次要隐患:
LeftOrRightRow方法缺少兜底返回值。如果传入的direction参数不是严格等于"right"或"left"(比如大小写错误、传参遗漏),函数不会执行任何return逻辑,返回值为undefined,后续访问newRow[0]时会抛出Cannot read properties of undefined的类型错误。
修复方案
第一步:调整2048.js的方法定义
不要在工具方法里直接引用外部作用域的可变变量,把squares作为参数传入方法彻底解决作用域问题,同时补充兜底返回逻辑,判断相等时优先用严格相等===避免隐式类型转换问题:
const game = Object.create(null); // 把依赖的squares作为入参传入,不直接引用外部变量 game.LeftOrRightRow = function (squares, i, direction){ let totalOne = squares[i].innerHTML; let totalTwo = squares[i+1].innerHTML; let totalThree = squares[i+2].innerHTML; let totalFour = squares[i+3].innerHTML; let row = [parseInt(totalOne), parseInt(totalTwo), parseInt(totalThree), parseInt(totalFour)] let filterRow = row.filter(num => num) let missing = 4 - filterRow.length let zeros = Array(missing).fill(0) if(direction === "right"){ return zeros.concat(filterRow) }else if (direction === "left"){ return filterRow.concat(zeros) } // 方向参数非法时默认返回原行,避免抛出错误 return row } export default Object.freeze(game);
第二步:调整main.js的调用逻辑
调用方法时把当前作用域下拿到的squares集合传入即可,注意要确保DOM加载完成后再获取squares元素集合:
import R from "../common/ramda.js"; import Json_rpc from "./Json_rpc.js"; import game from "../common/2048.js"; document.addEventListener("DOMContentLoaded", () => { // 补全squares的声明逻辑,例如: // const squares = document.querySelectorAll('.grid div') function moveLeftOrRight(direction){ for(let i=0; i <16; i++){ if(i%4 === 0){ // 调用时传入当前作用域的squares变量 let newRow = game.LeftOrRightRow(squares, i, direction) squares[i].innerHTML = newRow[0] squares[i+1].innerHTML = newRow[1] squares[i+2].innerHTML = newRow[2] squares[i+3].innerHTML = newRow[3] } } } })
修复后可以在调用方法前加一行
console.log(game),如果控制台能打印出包含LeftOrRightRow方法的冻结对象,就说明导入导出链路完全正常。日常写工具类方法时,尽量不要在方法内部直接引用外部作用域的可变变量,所有依赖都通过参数传入,既能避免跨模块的作用域问题,也方便后续调试复用。
内容的提问来源于stack exchange,提问作者Ax G
相关产品推荐
相关产品推荐

