JavaScript動(dòng)態(tài)執(zhí)行JS代碼的四種方法
今天來(lái)聊一聊javascript如何動(dòng)態(tài)執(zhí)行js代碼,我總結(jié)了以下四種方法。
第一種方式:eval
eval函數(shù)會(huì)將傳入的字符串作為js代碼執(zhí)行,并且返回最后一個(gè)表達(dá)式的值。
eval('console.log(1)') // 1
eval的特點(diǎn):
- 同步執(zhí)行;
- 執(zhí)行環(huán)境為當(dāng)前作用域。
let a = 1;
function test() {
let a = 2;
eval('console.log(a)'); // 2
}
test();
使用eval需要注意:
- 安全風(fēng)險(xiǎn):任何時(shí)候都不要把用戶的輸入放入eval中執(zhí)行,可能被注入惡意代碼(如 eval('alert("XSS")'))。
- 性能差:eval 會(huì)中斷 JavaScript 引擎的優(yōu)化,導(dǎo)致執(zhí)行速度變慢。
- 可讀性和維護(hù)性差:動(dòng)態(tài)生成的代碼難以調(diào)試和維護(hù)。
第二種方式:new Function
可以通過(guò)構(gòu)造函數(shù)Function動(dòng)態(tài)創(chuàng)建一個(gè)函數(shù),然后主動(dòng)執(zhí)行它。
Function的函數(shù)簽名如下:
new Function([arg1, arg2, ...argN], functionBody)
其中,arg1, arg2, ...argN 是參數(shù)名,functionBody 是函數(shù)體。
let a = 1;
function test() {
let a = 2;
let fn = new Function('console.log(a)');
fn(); // 1
}
test();
new Function的特點(diǎn):
- 同步執(zhí)行;
- 執(zhí)行環(huán)境為全局作用域。
第三種方式:setTimeout
平常我們使用setTimeout第一個(gè)傳遞的是一個(gè)函數(shù),第二個(gè)參數(shù)是延遲執(zhí)行的時(shí)間。
setTimeout(functionRef, delay);
但其實(shí),setTimeout的第一個(gè)參數(shù)也可以是一個(gè)字符串,表示要執(zhí)行的代碼。
setTimeout('console.log(1)', 1000); // 1
來(lái)看看setTimeout執(zhí)行的作用域:
let a = 1;
function test() {
let a = 2;
setTimeout('console.log(a)', 1000); // 1
}
test();
所以setTimeout的特點(diǎn)如下:
- 異步執(zhí)行;
- 執(zhí)行環(huán)境為全局作用域。
第四種方式:script標(biāo)簽
在瀏覽器環(huán)境中,我們可以動(dòng)態(tài)創(chuàng)建script標(biāo)簽來(lái)執(zhí)行代碼。
var a = 1;
let script = document.createElement('script');
script.textContent = 'console.log(1)';
document.body.appendChild(script); // 1
這個(gè)都不用多說(shuō)了,肯定是全局作用域,所以script動(dòng)態(tài)執(zhí)行的特點(diǎn)如下:
- 同步執(zhí)行;
- 執(zhí)行環(huán)境為全局作用域。
應(yīng)用場(chǎng)景
動(dòng)態(tài)執(zhí)行js這個(gè)功能在業(yè)務(wù)開發(fā)幾乎不用用到,但在一些打包工具中會(huì)用到,比如webpack。
1. devtool使用eval
在webpack的配置文件webpack.config.js,有一項(xiàng)關(guān)于devtool的配置。
// webpack.config.js
module.exports = {
devtool: 'eval-source-map',
}
當(dāng)把devtool設(shè)置為eval-source-map時(shí),webpack打包出來(lái)的代碼會(huì)使用eval進(jìn)行包裹。
打包前的代碼:
// ./src/index.js console.log(1)
打包后的產(chǎn)物長(zhǎng)這樣,這里我使用的是webpack5。
// bundle.js
(() => {
var __webpack_modules__ = ({
"./src/index.js":
(() => {
eval("console.log(1)//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvaW5kZXguanMiLCJtYXBwaW5ncyI6IkFBQUEiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLWRlbW8vLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zb2xlLmxvZygxKSJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/index.js\n");
})
});
var __webpack_exports__ = {};
__webpack_modules__["./src/index.js"]();
})();
這樣的好處是:
source-map映射內(nèi)置在打包后的js文件中,不會(huì)生產(chǎn)單獨(dú)的source-map文件;- 由于是用
eval直接包裹,所以構(gòu)建速度快,適合開發(fā)環(huán)境調(diào)試使用。
順便提一下,開發(fā)環(huán)境一般會(huì)使用eval-cheap-module-source-map,相比于eval-source-map:
cheap: 不生成列映射,只生成行映射,生成的映射文件體積更?。?/li>module: 生成映射文件時(shí),會(huì)保留模塊的路徑信息,方便調(diào)試。
生產(chǎn)環(huán)境如果需要調(diào)試的話,一般會(huì)使用全量的最完整的sourcemap,也就是devtool: 'source-map',這樣生成的映射文件雖然體積最大,但調(diào)試體驗(yàn)最好。
2. tapable中使用 new Function
在webpack的編譯過(guò)程中,tapable 實(shí)現(xiàn)了在編譯過(guò)程中的一種發(fā)布訂閱者模式的插件 Plugin 機(jī)制。在tapable的源碼中,就用到了new Function。
簡(jiǎn)單舉個(gè)例子:
// Tapable 動(dòng)態(tài)生成的調(diào)用函數(shù)(偽代碼)
const hook = new SyncHook(["arg1", "arg2"]);
hook.tap("plugin1", (arg1, arg2) => console.log(arg1, arg2));
hook.tap("plugin2", (arg1, arg2) => console.log(arg2, arg1));
這里用到了SyncHook做了一個(gè)監(jiān)聽,按傳統(tǒng)的發(fā)布訂閱的實(shí)現(xiàn)的話,內(nèi)部會(huì)用callbacks.forEach(fn => fn(...args)))進(jìn)行調(diào)用,每次觸發(fā)事件都需要?jiǎng)討B(tài)遍歷回調(diào)數(shù)組,性能較低,所以tapable用new Function做了優(yōu)化。
// 動(dòng)態(tài)生成的調(diào)用邏輯
const compiledFn = new Function("arg1", "arg2", `
plugin1Fn(arg1, arg2);
plugin2Fn(arg2, arg1);
`);
compiledFn("a", "b");
這里直接用new Function把邏輯進(jìn)行動(dòng)態(tài)組裝成一個(gè)函數(shù),節(jié)省了上下文開銷,實(shí)現(xiàn)更加高效的事件觸發(fā)邏輯,尤其是在處理大量鉤子(Hooks)和插件時(shí)。
總結(jié)
主要介紹了四種動(dòng)態(tài)執(zhí)行js的方法:
- eval: 同步,當(dāng)前作用域;
- new Function: 同步,全局作用域;
- script標(biāo)簽: 同步,全局作用域;
- setTimeout: 異步,全局作用域。
還簡(jiǎn)單介紹了動(dòng)態(tài)執(zhí)行的應(yīng)用場(chǎng)景,比如webpack的devtool配置使用到了eval,tapable中使用new Function。
以上就是JavaScript動(dòng)態(tài)執(zhí)行JS代碼的四種方法的詳細(xì)內(nèi)容,更多關(guān)于JavaScript動(dòng)態(tài)執(zhí)行JS代碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Javascript 圓角div的實(shí)現(xiàn)代碼
為什么要做圓角的div: 圓角div平滑美觀,某些情況下有比較不錯(cuò)的效果。比如說(shuō)要做一個(gè)報(bào)message的消息框,那么動(dòng)態(tài)的生成一個(gè)圓角div則很有意義。而對(duì)html樣式控制的css本身是不直接支持圓角div的。2009-10-10
小程序?qū)崿F(xiàn)瀑布流動(dòng)態(tài)加載列表
這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)瀑布流動(dòng)態(tài)加載列表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
uniapp中微信小程序與H5相互跳轉(zhuǎn)以及傳參詳解(webview)
在單位做項(xiàng)目的時(shí)候碰到一個(gè)需求,需要從微信小程序跳轉(zhuǎn)到H5頁(yè)面,下面這篇文章主要給大家介紹了關(guān)于uniapp中微信小程序與H5相互跳轉(zhuǎn)以及傳參的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08
原生js實(shí)現(xiàn)的金山打字小游戲(實(shí)例代碼詳解)
這篇文章主要介紹了原生js實(shí)現(xiàn)的金山打字小游戲,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
javascript笛卡爾積算法實(shí)現(xiàn)方法
這篇文章主要介紹了javascript笛卡爾積算法實(shí)現(xiàn)方法,實(shí)例分析了笛卡爾積算法的javascript實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
JavaScript的Object.defineProperty詳解
本篇文章給大家詳細(xì)講述了JavaScript的Object.defineProperty的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友參考學(xué)習(xí)下。2018-07-07
前端知識(shí)點(diǎn)之Javascript選擇輸入框confirm用法
這篇文章主要介紹了JavaScript中的confirm方法的基本用法、功能特點(diǎn)、注意事項(xiàng)及常見用途,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用js具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2025-02-02
OpenLayer學(xué)習(xí)之自定義測(cè)量控件
這篇文章主要為大家詳細(xì) 介紹了OpenLayer學(xué)習(xí)之自定義測(cè)量控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09
微信小程序五子棋游戲AI實(shí)現(xiàn)方法【附demo源碼下載】
這篇文章主要介紹了微信小程序五子棋游戲AI實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了五子棋游戲中人機(jī)對(duì)戰(zhàn)的AI原理及相關(guān)實(shí)現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2019-02-02
Firefox 無(wú)法獲取cssRules 的解決辦法
Firefox 無(wú)法獲取cssRules 的解決辦法...2006-10-10

