Rhino是基于Java的JavaScript解析器,實現(xiàn)了通過JavaScript程序訪問整個JavaAPI,Node是Google的V8 JavaScript解析器的一個特別版本,它在底層綁定了POSIXAPI,包括文件、進程、流和套接字,并側(cè)重于異步I/O、網(wǎng)絡(luò)和HTTP。
用Rhino腳本化Java
Rhino是一種用java編寫的JavaScript解釋器,Rhino能自動完成JavaScript原生類型與Java原生類型之間的相互轉(zhuǎn)換。
Rhino定義的全局函數(shù):
- print(x); //全局輸出函數(shù),將內(nèi)容輸出到控制臺
- version(170); //告訴Rhino需要使用JS 1.7的語言特性
- load(filename,...); //加載并執(zhí)行1個或多個JavaScript代碼文件
- readFile(file); //讀取文本文件,并以字符串的形式返回內(nèi)容
- readUrl(url); //讀取URL的原文內(nèi)容,并以字符串的形式返回內(nèi)容
- spawn(f); //運行f()或者在一個新線程中加載執(zhí)行文件f
- runCommand(cmd,[args...]); //使用O或多個命令行參數(shù)來運行系統(tǒng)命令
- quit(); //退出Rhino
Rhino會將Java包和類表示成JavaScript對象,因此可以將他們賦值給變量從而得到相應(yīng)的短名:
var ArrayList = java.util.ArrayList; //為類創(chuàng)建短名
importClass(java.util.HashMap); //其等同于:var HashMap = java.util.HashMap
Java類能使用new進行實例化,就像JavaScript類一樣:
var f = new java.io.file("/tmp/test");
var out = new java.io.FileWiter(f);
Rhino讓JavaScript的instanceof運算符能用于Java對象和類:
f instanceof java.io.file; //true
out instanceof java.io.reader //false:他是writer而非Reader
out instanceof java.io.Vloseable //true:Writer實現(xiàn)Closeable
- Rhino也允許JavaScript代碼查詢、設(shè)置Java類的靜態(tài)字段和Java對象的實例字段。Java類通常利用getter和setter方法避免定義公共字段。
- 使用for/in循環(huán)遍歷Java類和對象的方法、字段和屬性,但是不能枚舉包中的類。
Java編程經(jīng)常涉及實現(xiàn)接口,每個事件處理程序都必須實現(xiàn)事件監(jiān)聽接口:
//用同樣的方式擴展抽象類
//當接口只有一個方法,可以使用一個函數(shù)取而代之
//如果需要一個對象實現(xiàn)多重接口,則使用JavaAdapter
Rhino會俺需要自動轉(zhuǎn)換為原始數(shù)字、布爾值和null。
用Node實現(xiàn)異步I/O
Node是基于C++的告訴JavaScript解釋器,綁定了用于進程、文件和網(wǎng)絡(luò)套接字底層的Unix API,還綁定了HTTP客戶端和服務(wù)器API。
由于API是異步的,因此Node依賴時間處理程序,其通常使用嵌套函數(shù)和閉包來實現(xiàn) 。
Node重要函數(shù):
console.log() //調(diào)試輸出到控制臺
require()(相當于Rhino里的load()) //加載模塊,并返回其API對象
Node在process名字空間中定義了其他重要的全局屬性:
- process.version //Node的版本字符串信息
- process.argv //"node"命令行的數(shù)組參數(shù),argv[0]是“node”;
- process.env //環(huán)境變量對象 例如:process.env.PATH
- process.pid //進程id
- process.getuid() //返回用戶id
- process.cwd() //返回當前的工作目錄
- process.chdir() //改變目錄
- process.exit() //退出(運行shutdown命令之后)
由于Node的函數(shù)和方法都是異步的,因此當他們等待運算完成時并不產(chǎn)生阻塞。非阻塞方法的返回值無法返回異步運算的結(jié)果。
如果想要獲取結(jié)果,就必須提供一個Node能夠調(diào)用的一個函數(shù),在某些情況下,只需簡單的把函數(shù)作為參數(shù)傳遞,Node會適時調(diào)用它,在另外一些情況下,可以利用Node的事件機制。
Node對象產(chǎn)生事件,定義on()方法來注冊處理程序,當傳入?yún)?shù)時,將時間類型作為第一個參數(shù),處理程序函數(shù)作為第二參數(shù)。
不同的時間類型傳遞給處理程序函數(shù)的參數(shù)不同:
- emitter.on(nama,f) //emitter注冊f函數(shù)處理name事件
- emitter.addListener(nama,f) //addListener()和on()是用一個方法
- emitter.once(name,f) //只執(zhí)行一次,然后f會自動刪除
- emitter.listeners(name) //返回時間處理函數(shù)組成的數(shù)組
- emitter.removeListener(name,f) // 注銷事件處理程序f
- emitter.removeAllListeners(name) //移除name事件的所有處理程序
舉例:
//"exit"事件在Node退出之前發(fā)送
process.on("exit",function() { console.log("Goodbye"); });
Node的設(shè)計目標是高性能I/O,因此其流API常被用到。
從文件和網(wǎng)絡(luò)套接字中得到流對象:
//輸入流
- s.on("data",f); //當數(shù)據(jù)可用時,把它作為參數(shù)傳給f()
- s.on("end",f); //當不在有數(shù)據(jù)達到,在文件結(jié)束(EOF)時會觸發(fā)"end"事件
- s.on("error",f); //如果發(fā)生錯誤,把異常傳給f()
- s.readable //如果他是依舊打開的可讀流,返回true
- s.pause(); //暫停"data"時間,例如:為了限制上傳
- s.resume(); //再次恢復(fù)
//如果想把字符串傳給"data"事件處理程序,請指定編碼
- s.setEncoding(enc) //如何對字節(jié)編碼:"utf8"、"ascii"或"base64"
可寫流比可讀流的的核心事件少,使用write()方法發(fā)送數(shù)據(jù),當所有數(shù)據(jù)寫入完畢后使用end()方法結(jié)束流。write方法不會阻塞,若Node方法無法立即寫入數(shù)據(jù)而不得不在內(nèi)部緩存它,則write()返回false.
注冊"drain"事件的處理程序:
//輸出流
- s.write(buffer); //寫入二進制數(shù)據(jù)
- s.write(string,encoding) //寫入字符串數(shù)據(jù),默認編碼是"utf-8"
- s.end() //結(jié)束流
- s.end(buffer); //寫入最后的二進制數(shù)據(jù)塊并結(jié)束
- s.end(str,encoding) //寫入最后的字符串并結(jié)束所有流
- s.writeable; //如果流依舊打開且可寫入,返回TRUE
- s.on("drain",f) // 當內(nèi)部緩沖區(qū)為空,調(diào)用f()