JS函数内对象修改是否为局部?异步回调对象属性变更不生效问题求解
JS函数内对象修改生效范围说明
JS中函数内对对象属性的修改不是仅局部生效的,你遇到的现象和对象作用域无关,本质是Node.js异步执行机制导致的问题,具体原因和解决方案如下:
问题原因分析
1. 第二个console.log先打印的原因
child_process.exec是非阻塞异步API,调用该方法时,Node.js会将子进程执行任务放到后台处理,主线程不会等待子进程运行结束,会继续执行后续代码,所以exec外部的console.log会先执行。只有当子进程运行结束后,传入exec的回调函数才会被推入事件队列等待主线程执行,因此回调内部的第一个console.log打印时机更晚。
2. 两次console.log输出存在差异的原因
外部console.log执行时,exec的回调函数还未触发,此时实例的_output、_error属性还是构造函数中初始化的Server not responding值。等到回调执行时,已经对这两个属性完成了赋值修改,因此回调内的console.log会打印修改后的值。你观察到的“状态变更无法持久生效”是因为你在回调执行前就读取了属性,并非修改仅在局部生效。
问题解决方案
可以通过Promise封装异步操作,配合async/await等待异步任务完成后再读取属性,同时修复原代码中writeFile异步导致的可能文件未写完就执行命令的隐藏问题:
class Code { static list = require("./settings.json"); static fs = require("fs"); constructor( code="print('Hello World')",//default code lang = "py",//language fn = "test",//file name args = "" //arguments ) { this.code = code; const path = require('path'); this.fn = path.join(__dirname,fn) + "." + lang;//for platform independence this.run_com = Code.replace(Code.list[lang], this.fn, args); this._output="Server not responding"; this._error="Server not responding"; } file_exists() { return Code.fs.existsSync(`${this.fn}`); } // 改造make_file返回Promise,处理异步写入逻辑 make_file() { return new Promise((resolve, reject) => { Code.fs.writeFile(`${this.fn}`, this.code, (err) => { if (err) return reject(err); resolve(); }); }) } // 封装exec逻辑为内部方法 _runExec(resolve, reject) { const { exec } = require("child_process"); exec(this.run_com, (error, stdout, stderr) =>{ if(error) { this._error = error.message; this._output = stdout; return reject(error); } this._output=stdout; this._error = stderr === "" ? "No error" : stderr; resolve(this); }); } // 改造run_file返回Promise run_file() { return new Promise(async (resolve, reject) => { try { if(!this.file_exists()) { await this.make_file(); } this._runExec(resolve, reject); } catch (err) { reject(err); } }) } get error() { return this._error; } get output() { return this._output; } static replace( s //replace {} ) { const arg = Object.values(arguments).slice(1); //convert obj to array removing s for (let i in arg) { s = s.replace("{}", arg[i]); } return s; } } // 调用方式 async function runCode() { const a = new Code("print('exd')"); await a.run_file(); // 等待异步执行完成后再读取属性,可拿到正确值 console.log('输出:', a.output); console.log('错误:', a.error); } runCode();
改造后所有异步操作完成后才会返回结果,此时读取实例属性就能拿到修改后的值,确认对象属性的修改是全局生效的。
内容的提问来源于stack exchange,提问作者Bhaumik_Tandan
相关产品推荐
相关产品推荐

