第1种方法是使用eval函数,它会将传入的字符串作为JS代码同步执行,并且运行在当前作用域中。比如在函数内定义局部变量a=2,调用eval('console.log(a)')会打印2,说明它取的是当前作用域的变量。但eval存在明显缺陷:安全上,若将用户输入直接传入,可能被注入恶意代码(比如eval('alert("XSS")'));性能上,会中断JS引擎的优化,导致执行速度变慢;另外,动态生成的代码难以调试和维护,可读性较差。
第2种方法是通过new Function构造函数动态创建函数。它的语法是new Function(参数列表, 函数体),创建后主动调用即可。这种方法同步执行,但运行在全局作用域中。比如函数内定义局部变量a=2,用new Function创建的函数执行console.log(a),会打印全局的a=1。这种方法在框架插件机制中很有用,比如webpack的tapable库用它将多个插件逻辑组装成一个函数,避免遍历回调数组的性能开销,提升事件触发效率。
第3种方法是利用setTimeout的字符串参数特性。通常setTimeout的第一个参数是函数,但也可以是字符串,它会在延迟后异步执行这段代码,且运行在全局作用域中。比如函数内定义局部变量a=2,调用setTimeout('console.log(a)', 1000),会先打印函数外的sync,再打印全局的a=1,说明它是异步执行的。这种方法适合需要延迟执行的场景,但要注意异步带来的顺序问题。
第4种方法是动态创建script标签。在浏览器环境中,通过document.createElement('script')生成script元素,设置其textContent为要执行的代码字符串,再将元素插入document.body或document.head中。这种方法同步执行,运行在全局作用域中。比如插入的代码会立即执行,打印全局变量的值。这种方法的优势是直接模拟浏览器加载脚本的过程,但会生成额外的DOM元素。
这些方法在实际开发中有不同的应用场景。比如webpack的devtool配置中,eval-source-map会用eval包裹打包后的代码,提升构建速度;低代码平台中,动态执行代码可以实现用户自定义的逻辑;框架插件机制中,new Function用于优化事件触发的性能。需要注意的是,使用动态执行代码时,要优先考虑安全和性能,避免将用户输入直接传入eval等方法,尽量选择更高效的方式如new Function或script标签。
