跳坑之js調(diào)用另一個js文件中函數(shù)

最近在做electron的內(nèi)容,但是踩了很多坑,其中一個坑:

問題背景:對于進(jìn)程間的通信,實現(xiàn)帶參數(shù)的輸入輸出,就是直接寫一些函數(shù),可以實現(xiàn)其他文件對于函數(shù)的調(diào)用

問題內(nèi)容:其中一個js是實現(xiàn)功能函數(shù)(renderer.js),也就是帶參數(shù)的輸入輸出,另一個js是調(diào)用這些功能(hello.js),那么如何實現(xiàn)這個調(diào)用過程呢?

我的hello.js和renderer.js在同一個文件夾下面。

來嘗試一下網(wǎng)上的方法:

方法一:在html文件中body標(biāo)簽后加入js文件,在index.html中的body標(biāo)簽(</body>與</html>之間插入所引用的hello.js),代碼如下:

<html>
  <head>
    <!---head內(nèi)容--->
  </head>
    <body>
      <!---body內(nèi)容--->
    </body>
    <!---在body后面添加所引用的js文件-->
    <script language="JAVASCRIPT" src='hello.js'></script>
</html>

然后,對于hello.js文件,為了調(diào)用renderer.js中的函數(shù),需要在hello.js中添加以下代碼:

new_element = document.createElement("script");
new_element.setAttribute("type","text/javascript");
new_element.setAttribute("src", "renderer.js");
document.body.appendChild(new_element);

function testBtn(struct, buttonId, msg){
    //renderer.js中函數(shù)為test
    test(struct, buttonId, msg);
  }
testBtn('click', 'asynchronous message', 'ping');

對于代碼,網(wǎng)上解釋如下:
讓我們來分析一下關(guān)鍵的幾句代碼:首先,我們利用document.createElement("script")生成了一個script的標(biāo)簽,設(shè)置其type屬性為text/javascript,src為renderer.js(這里的renderer.js同hello.js放在同一個目錄,也可放在不同的目錄)。最后將這個標(biāo)簽動態(tài)地加入body中。如此一來,我們就可以調(diào)用到不同js文件中的方法了。

注意:<script language="JAVASCRIPT" src='b.js'></script>一定要放在body下面。
因為hello.js中用到了body(document.body.appendChild(new_element);)
如果將引如hello.js的代碼放在body上面,也就是說,
進(jìn)入頁面后,還沒有生成body就已經(jīng)執(zhí)行hello.js里的document.body.appendChild(new_element);了。
這時body不存在就會拋javascript錯誤。

而實際效果呢,并不行(至少我沒有測試成功,目前還不知道原因出現(xiàn)在哪里,求告知?。。?/p>

方法二:在調(diào)用者程序的開始直接加入要被調(diào)用的js文件,代碼如下:

//加入下面的代碼
document.write("<script language=javascript src='./renderer.js'></script>");
//調(diào)用函數(shù)
test('click', 'asynchronous message', 'ping');

結(jié)果呵呵了。。。。。還是不行。。。。。又是一頭霧水

方法三:在html文件中加入兩個腳本程序,注意,加入的位置在</head>和<body>兩個標(biāo)簽之間,(也有的在<body></body>兩個標(biāo)簽之間加入的),代碼如下:

</head>
  <script src="hello.js"></script>
  <script src="renderer.js"></script>
  <body>

之后在hello.js中直接調(diào)用函數(shù)就行。

test('click', 'asynchronous message', 'ping');

然后呢?如果說前兩種方法不行我還信了,畢竟沒有看到執(zhí)行結(jié)果,但是第三種人家明明成功了,而且兩種加入的方法都成功了,到我這兒走不通了,幾近崩潰。。。

冷靜冷靜,如果你試了試前面的方法也不行,一定要淡定,我也不知道怎么想的,然后試了一下下面這個方法。。竟然成功了??!

方法四:首先,在方法三的基礎(chǔ)上,在html中直接利用require,將兩個js文件直接加載進(jìn)去,然后就可以實現(xiàn)調(diào)用了。
html中的代碼:

//首先,方法三中的加入內(nèi)容不變
</head>
  <script src="hello.js"></script>
  <script src="renderer.js"></script>
  <body>
    <!--- body內(nèi)容--->

    //方法四加入的內(nèi)容
    <script>
    // You can also require other files to run in this process
    require('./renderer.js')
    require('./hello.js')
    </script>
  </body>
 </head>

至于調(diào)用函數(shù)的代碼,與方法三一樣,直接在hello.js中調(diào)用即可。

解釋解釋,知其然必須知其所以然(個人理解):
對于方法三,為什么不行呢?我打開electron調(diào)試工具的時候,第一次加載頁面時,輸出了這個信息(極為重要):
test is not defined----我的test函數(shù)沒有被定義,為什么沒有被定義,我明明已經(jīng)寫好了的,看一下方法三中的加載順序,先加載的hello.js,之后加載的renderer.js,也就是說,先加載了的hello.js中的test方法沒有被定義,然后自然函數(shù)執(zhí)行不成功。
看了看程序,發(fā)現(xiàn)方法四的加載順序與方法三完全相反,在查看了文檔之后,發(fā)現(xiàn)方法三與方法四的加載的最終結(jié)果并沒有什么不同,只不過:require引入的的文件,內(nèi)部聲明的最外層變量不屬于全局變量,而script引入的屬于全局變量。

最重要的信息:
如果用script引入需要考慮引入順序,避免變量沖突和前置依賴??紤]順序,考慮順序,考慮順序,重要的事情說三遍(之后自己也試了試,把方法三的順序顛倒,發(fā)現(xiàn)可以,之所以沒刪除這些內(nèi)容,是想記得更清,也避免讓更多的人入坑)

哈哈哈哈,腦子真的被門給夾了,這個坑跳的真的值(說的我自己都信了)
不過,以方法三加載腳本程序的方法并不好:
(來自阮一峰先生的日志:http://www.ruanyifeng.com/blog/2012/11/require_js.html) 首先,加載的時候,瀏覽器會停止網(wǎng)頁渲染,加載文件越多,網(wǎng)頁失去響應(yīng)的時間就會越長;其次,由于js文件之間存在依賴關(guān)系,因此必須嚴(yán)格保證加載順序(比如上例的1.js要在2.js的前面),依賴性最大的模塊一定要放到最后加載,當(dāng)依賴關(guān)系很復(fù)雜的時候,代碼的編寫和維護(hù)都會變得困難。

最后,介紹一種我最喜歡的方式,對于功能的封裝這種方式應(yīng)該再好不過了,exports和require大法好。

在nodejs中,提供了exports 和 require 兩個對象,其中 exports 是模塊公開的接口,require 用于從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。而在exports拋出的接口中,我們可以拋出變量或者函數(shù)。
如果你希望你的模塊就想為一個特別的對象類型,請使用module.exports;如果希望模塊成為一個傳統(tǒng)的模塊實例,請使用exports.xx方法;module.exports才是真正的接口,exports只不過是它的一個輔助工具。最終返回給調(diào)用的是module.exports而不是exports。
總而言之,二者的關(guān)系是:

    exports 是指向的 module.exports 的引用,二者指向同一塊內(nèi)存
    module.exports 初始值為一個空對象 {},所以 exports 初始值也是 {}
    require() 返回的是 module.exports 而不是 exports

下面給出代碼:
renderer.js中使用exports導(dǎo)出函數(shù):

//在這里面寫好函數(shù)的封裝,然后在hello.js中調(diào)用
var test = function(struct, buttonId, msg){
    const asyncMsgBtn = document.getElementById(buttonId);
    asyncMsgBtn.addEventListener(struct, function(){
        switch(struct){
            case 'click':
                ipc.send('asynchronous-message', msg);
                console.log("調(diào)用成功");
                break;
            default:
                console.log('Error!!!')
        }
    })
}
//這種方式是成功的
exports.test = test;
//這種方式也是可以得
//module.exports.test = test;

而hello.js中對于代碼的使用如下:

//利用require加載模塊
const renderer = require('./renderer')

renderer.test('click', 'asynchronous message', 'ping');
renderer.test('click', 'changeView', 'change');

可以說,這種方式完全符合我們程序封裝的概念,思路統(tǒng)一,結(jié)構(gòu)規(guī)整,個人最愛。而且,上面的程序中兩種方法都可以輸出成功,其原因就在于:
exports變量是在模塊的文件級別作用域內(nèi)有效的,它在模塊被執(zhí)行前被賦于 module.exports 的值。它有一個快捷方式,以便 module.exports.f = ... 可以被更簡潔地寫成exports.f = ...。 注意,就像任何變量,如果一個新的值被賦值給exports,它就不再綁定到module.exports(其實是exports.xx會自動掛載到?jīng)]有命名沖突的module.exports.xx)
而且,對于require函數(shù),exports只是函數(shù)內(nèi)部一個局部變量,最后返回的仍是module.exports,這應(yīng)該就是exports稱為module.exports的引用所在。代碼內(nèi)容如下(應(yīng)該很清晰了):

function require(...) {  
  var module = { exports: {} };
  ((module, exports) => {
    // 你的被引入代碼 Start
    // var exports = module.exports = {}; (默認(rèn)都有的)
    function some_func() {};
    exports = some_func;
    // 此時,exports不再掛載到module.exports,
    // export將導(dǎo)出{}默認(rèn)對象
    module.exports = some_func;
    // 此時,這個模塊將導(dǎo)出some_func對象,覆蓋exports上的some_func    
     // 你的被引入代碼 End
  })(module, module.exports);
 // 不管是exports還是module.exports,最后返回的還是module.exports 
  return module.exports;
}

最后,這個坑跳的真的值?。。?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容