摘要:例如允許的對(duì)象默認(rèn)情況下,通過使用內(nèi)置宏將核心對(duì)象和方法映射到。例如這被轉(zhuǎn)換為以下代碼類可以定義構(gòu)造函數(shù),具有超類,并且可以像在中一樣實(shí)例化。因此,它不違反原則。用于聲明該對(duì)象可以用作構(gòu)造函數(shù)。
這個(gè)工具可以將java代碼轉(zhuǎn)為js代碼,從而可以使用java編寫前端代碼 如果排版看著費(fèi)勁可以下載下方html,打開html后使用google翻譯
JSweet語(yǔ)言規(guī)范
版本:2.x(快照)
作者:Renaud Pawlak
作者助理:Louis Grignon
JSweet JavaDoc API:http://www.jsweet.org/core-api-javadoc/
注意:此降價(jià)是從Latex源文件自動(dòng)生成的。不要直接修改。
內(nèi)容
基本概念
核心類型和對(duì)象
類
接口
無類型對(duì)象(地圖)
枚舉
全局
可選參數(shù)和重載
橋接外部JavaScript元素
例子
寫定義規(guī)則(又名橋梁)
無法訪問
混入
從現(xiàn)有的TypeScript定義生成JSweet糖果
輔助類型
功能類型
對(duì)象類型
字符串類型
元組類型
聯(lián)盟類型
交叉類型
語(yǔ)義
主要方法
初始化器
數(shù)組初始化和分配
異步編程
姓名沖突
測(cè)試對(duì)象的類型
lambda表達(dá)式中的變量作用域
這個(gè)范圍
打包
使用您的文件,無需任何包裝
為瀏覽器創(chuàng)建捆綁包
包裝模塊
根包
包裝JSweet jar(糖果)
擴(kuò)展轉(zhuǎn)換器
核心注釋
集中注釋 jsweetconfig.json
使用適配器進(jìn)行編程調(diào)整
擴(kuò)展示例
附錄1:JSweet轉(zhuǎn)換器選項(xiàng)
附錄2:包裝和靜態(tài)行為
調(diào)用main方法時(shí)
靜態(tài)和繼承依賴項(xiàng)
基本概念
本節(jié)介紹JSweet語(yǔ)言的基本概念。必須記住,JSweet作為Java-to-JavaScript轉(zhuǎn)換器,是編譯時(shí)Java的擴(kuò)展,并在運(yùn)行時(shí)作為JavaScript執(zhí)行。JSweet旨在通過盡可能多地尊重Java語(yǔ)義來實(shí)現(xiàn)Java和JavaScript之間的權(quán)衡,但不會(huì)失去與JavaScript的互操作性。因此,在某種程度上,JSweet可以看作是Java和JavaScript之間的融合,試圖用一種獨(dú)特且一致的語(yǔ)言來充分利用這兩個(gè)世界。在某些情況下,很難充分利用這兩個(gè)世界,JSweet可以提供方便實(shí)用的選擇。
因?yàn)镴Sweet是一個(gè)開放的JavaScript轉(zhuǎn)換器,所以用戶可以毫不費(fèi)力地調(diào)整JavaScript生成,從而做出除默認(rèn)選擇之外的其他選擇來將Java映射到JavaScript。例如,如果JSweet實(shí)現(xiàn)Java映射的方式不適合您的上下文或用例,則可以編寫JSweet擴(kuò)展以覆蓋默認(rèn)策略。第6節(jié)詳細(xì)介紹了編程和激活JSweet擴(kuò)展 。
核心類型和對(duì)象
JSweet允許使用原始Java類型,核心Java對(duì)象(在java.lang許多JDK類中定義(特別是java.util但不僅僅是),以及在def.js包中定義的核心JavaScript對(duì)象 。接下來,我們描述了這些核心類型的使用和對(duì)象。
原始Java類型
JSweet允許使用Java原始類型(和相關(guān)的文字)。
int,byte,short,double,float被全部轉(zhuǎn)換成JavaScript數(shù)字(打字稿number類型)。精密通常不會(huì)在JSweet無所謂,但是,鑄造int,byte或 short強(qiáng)制四舍五入到合適的長(zhǎng)度整數(shù)數(shù)量。
char遵循Java類型規(guī)則,但由轉(zhuǎn)換器轉(zhuǎn)換為JavaScript string。
boolean對(duì)應(yīng)于JavaScript boolean。
java.lang.String對(duì)應(yīng)于JavaScript string。(不是說原始類型,但是是不可變的并且在Java中用作字符串文字的類)
轉(zhuǎn)換的直接后果是,JSweet中并不總是可以使用數(shù)字或字符/字符串安全地重載方法。例如,方法pow(int, int)以及 pow(double, double)可能引起過載的問題。使用JSweet上下文,轉(zhuǎn)換器將能夠選擇正確的方法,但JavaScript互操作性可能是一個(gè)問題。總之,由于沒有區(qū)別n instanceof Integer和n instanceof Double (它既裝置typeof n === ’number’)調(diào)用pow(number, number) 從JavaScript將隨機(jī)選擇一個(gè)實(shí)現(xiàn)或其他。這不應(yīng)該總是一個(gè)問題,但在某些特殊情況下,它可能會(huì)產(chǎn)生微妙的錯(cuò)誤。請(qǐng)注意,在這些情況下,程序員將能夠調(diào)整JavaScript生成,如第6節(jié)中的完整解釋 。
有效聲明的示例:
// warning "==" behaves like JavaScript "===" at runtime
int i = 2;
assert i == 2;
double d = i + 4;
assert d == 6;
String s = "string" + "0" + i;
assert s == "string02";
boolean b = false;
assert !b;
該==運(yùn)營(yíng)商的行為類似于JavaScript的嚴(yán)格等于運(yùn)算符 ===,使其接近Java語(yǔ)義。同樣,!=映射到!==。在將對(duì)象與null文字進(jìn)行比較時(shí),該行為有一個(gè)例外。在這種情況下,JSweet轉(zhuǎn)換為松散的相等運(yùn)算符,以便程序員看不到null和之間的區(qū)別undefined(這在JavaScript中是不同的,但它可能會(huì)讓Java程序員感到困惑)。要控制JSweet是否生成嚴(yán)格或松散的運(yùn)算符,可以使用以下輔助方法:jsweet.util.Lang.$strict和 jsweet.util.Lang.$loose。在這樣的宏中包裝比較運(yùn)算符將強(qiáng)制JSweet生成嚴(yán)格或松散的運(yùn)算符。例如:
import static jsweet.util.Lang.$loose;
[...]
int i = 2;
assert i == 2; // generates i === 2
assert !((Object)"2" == i);
assert $loose((Object)"2" == i); // generates "2" == i
允許的Java對(duì)象
默認(rèn)情況下,JSweet通過使用內(nèi)置宏將核心Java對(duì)象和方法映射到JavaScript。這意味著Java代碼直接替換為實(shí)現(xiàn)類似行為的有效JavaScript代碼。為大多數(shù)有用的核心Java類(java.lang,java.util)實(shí)現(xiàn)了默認(rèn)映射。在可能的情況下(當(dāng)它有意義時(shí)),為其他JDK類實(shí)現(xiàn)一些部分映射,例如輸入和輸出流,區(qū)域設(shè)置,日歷,反射等。
使用默認(rèn)行為,我們可以指出以下限制:
除了某些特定的上下文之外,通常不可能擴(kuò)展JDK類。如果需要擴(kuò)展JDK類,應(yīng)該考慮重構(gòu)您的程序,或者使用允許它的JavaScript運(yùn)行時(shí)(例如J4TS)。
Java反射API(java.lang.reflect)僅限于非常基本的操作。可以訪問類和成員,但無法訪問類型。可以使用更完整的Java反射支持,但需要JSweet擴(kuò)展。
目前還不支持Java 8流,但部分支持它們很簡(jiǎn)單(歡迎貢獻(xiàn))。
有效聲明的示例:
Integer i = 2;
assert i == 2;
Double d = i + 4d;
assert d.toString() == "6";
assert !((Object) d == "6");
BiFunction
assert "bc" == f.apply("abc", 1);
獲得更多Java API
使用JSweet,可以添加在JavaScript中實(shí)現(xiàn)Java API的運(yùn)行時(shí),以便程序員可以訪問更多Java API,從而在Java和JavaScript之間共享相同的代碼。為JSweet實(shí)現(xiàn)Java API的核心項(xiàng)目是J4TS(https://github.com/cincheo/j4ts),它包含一個(gè)非常完整的java.util.*類和其他核心包的實(shí)現(xiàn)。J4TS基于GWT的JRE仿真的一個(gè)分支,但它適合用JSweet編譯。程序員可以將J4TS用作Maven存儲(chǔ)庫(kù)中的常規(guī)JavaScript庫(kù)。
雖然J4TS不能直接執(zhí)行Java核心類型的使用JavaScript的人發(fā)生沖突(Boolean,Byte,Short,Integer, Long,F(xiàn)loat,Double,Character,String),J4TS有助于通過為每個(gè)類提供傭工(支持他們的靜態(tài)部分javaemul.internal.BooleanHelper,javaemul.internal.ByteHelper...)。當(dāng)JSweet轉(zhuǎn)換器在java.lang.T不支持作為內(nèi)置宏的類型上遇到靜態(tài)Java方法時(shí) ,它會(huì)委托給 javaemul.internal.THelper,它可以為給定的靜態(tài)方法提供JavaScript實(shí)現(xiàn)。這樣,通過使用J4TS,程序員可以使用更多的核心JRE API。
Java數(shù)組
數(shù)組可以在JSweet中使用,并轉(zhuǎn)換為JavaScript數(shù)組。數(shù)組初始化,訪問和迭代都是有效的語(yǔ)句。
int[] arrayOfInts = { 1, 2, 3, 4};
assert arrayOfInts.length == 4;
assert arrayOfInts[0] == 1;
int i = 0;
for (int intItem : arrayOfInts) {
assert arrayOfInts[i++] == intItem;
}
核心JavaScript API
核心JavaScript API已定義def.js(完整文檔可在http://www.jsweet.org/core-ap...)。主要的JavaScript類是:
def.js.Object:JavaScript Object類。JavaScript對(duì)象函數(shù)和屬性的共同祖先。
def.js.Boolean:JavaScript布爾類。布爾值的包裝器。
def.js.Number:JavaScript Number類。數(shù)值的包裝器。
def.js.String:JavaScript String類。字符串的包裝器和構(gòu)造函數(shù)。
def.js.Function:JavaScript函數(shù)類。函數(shù)的構(gòu)造函數(shù)。
def.js.Date:JavaScript Date類,它支持基本存儲(chǔ)和檢索日期和時(shí)間。
def.js.Array
def.js.Error:JavaScript錯(cuò)誤類。這個(gè)類實(shí)現(xiàn) java.lang.RuntimeException并且可以被拋出并被try ... catch語(yǔ)句捕獲。
使用JavaScript框架時(shí),程序員應(yīng)該在大多數(shù)時(shí)間使用此API,這與HTML5兼容并遵循JavaScript最新支持的版本。但是,對(duì)于需要與Java文字(數(shù)字,布爾值和字符串)一起使用的對(duì)象,java.lang 建議使用包類。例如,jQuery API聲明 $(java.lang.String)而不是$(def.js.String)。這允許程序員使用文字來編寫表達(dá)式,例如$("a")(用于選擇文檔中的所有鏈接)。
使用JSweet,程序員可以根據(jù)需要輕松地從Java切換到JavaScript API(反之亦然)。在jsweet.util.Lang 類定義方便的靜態(tài)方法投來回核心Java對(duì)象到其相應(yīng)的JavaScript對(duì)象。例如,該 string(...)方法將允許程序員從Java切換到JavaScript字符串,反之亦然。
import static jsweet.util.Lang.string;
// str is a Java string, but is actually a JavaScript string at runtime
String str = "This is a test string";
// str is exactly the same string object, but shown through the JS API
def.js.String str2 = string(str);
// valid: toLowerCase it defined both in Java and JavaScript
str.toLowerCase();
// this method is not JS-compatible, so a macro generates the JS code
str.equalsIgnoreCase("abc");
// direct call to the JS substr method on the JavaScript string
string(str).substr(1);
// or
str2.substr(1);
注意:例如,對(duì)于JavaScript客戶端和Java服務(wù)器之間的代碼共享,最好只使用Java API并避免使用JavaScript API。JavaScript API將編譯有效的Java字節(jié)碼,但嘗試在JVM上執(zhí)行它們會(huì)引起不滿意的鏈接錯(cuò)誤。
這是另一個(gè)示例,顯示了使用該array方法訪問pushJavaScript數(shù)組中可用的方法。
import static jsweet.util.Lang.array;
String[] strings = { "a", "b", "c" };
array(strings).push("d");
assert strings[3] == "d";
類
JSweet中的類完全支持所有類型的Java類聲明。例如:
public class BankAccount {
public double balance = 0; public double deposit(double credit) { balance += credit; return this.balance; }
}
這被轉(zhuǎn)換為以下JavaScript代碼:
var BankAccount = (function () {
function BankAccount() { this.balance = 0; } BankAccount.prototype.deposit = function(credit) { this.balance += credit; return this.balance; }; return BankAccount;
})();
類可以定義構(gòu)造函數(shù),具有超類,并且可以像在Java中一樣實(shí)例化。與Java類似,JSweet中允許使用內(nèi)部類和匿名類(從1.1.0版開始)。JSweet支持靜態(tài)和常規(guī)內(nèi)部/匿名類,它們可以與封閉類共享狀態(tài)。仍然像在Java中一樣,匿名類可以訪問其作用域中聲明的最終變量。例如,以下聲明在JSweet中有效,并且將在運(yùn)行時(shí)模仿Java語(yǔ)義,以便Java程序員可以受益于Java語(yǔ)言的所有功能。
abstract class C {
public abstract int m();
}
public class ContainerClass {
// inner class public class InnerClass { public I aMethod(final int i) { // anonymous class return new C() { @Override public int m() { // access to final variable i return i; } } } }
}
接口
在JSweet中,可以像在Java中一樣使用接口。但是,與Java相反,沒有關(guān)聯(lián)的類可用作運(yùn)行時(shí)。使用接口時(shí),JSweet會(huì)生成代碼來模擬特定的Java行為(例如instanceof在接口上)。
JSweet支持Java 8靜態(tài)和默認(rèn)方法。但是,默認(rèn)方法到目前為止都是實(shí)驗(yàn)性的,你應(yīng)該自擔(dān)風(fēng)險(xiǎn)使用它們。
在JSweet中,接口更類似于TypeScript中的接口而不是Java中的接口。這意味著它們必須被視為對(duì)象簽名,它可以指定函數(shù),還可以指定屬性。為了在定義接口時(shí)允許使用字段作為屬性,JSweet允許使用帶注釋的常規(guī)類@jsweet.lang.Interface。例如,以下接口鍵入Point具有2個(gè)屬性的對(duì)象。
@Interface
public class Point {
public double x; public double y;
}
對(duì)于Java程序員來說,這可能看起來像是一種非常奇怪的方法來定義一個(gè)對(duì)象,但是你必須記住它不是一個(gè)類,而是一個(gè)JavaScript對(duì)象的類型。因此,它不違反OOP原則。我們可以創(chuàng)建一個(gè)在界面后鍵入的JavaScript對(duì)象。請(qǐng)注意,以下代碼實(shí)際上并未創(chuàng)建Point 接口的實(shí)例,而是創(chuàng)建符合接口的對(duì)象。
Point p1 = new Point() {{ x=1; y=1; }};
此對(duì)象創(chuàng)建機(jī)制是TypeScript / JavaScript機(jī)制,不應(yīng)與匿名類混淆,匿名類是類似Java的構(gòu)造。因?yàn)镻oint有注釋@Interface,轉(zhuǎn)換后的JavaScript代碼類似于:
var p1 = Object.defineProperty({ x:1, y:1 }, "_interfaces", ["Point"]);
請(qǐng)注意,對(duì)于每個(gè)對(duì)象,JSweet會(huì)跟蹤其創(chuàng)建的接口以及其類實(shí)現(xiàn)的所有可能接口。此接口跟蹤系統(tǒng)實(shí)現(xiàn)為一個(gè)名為的特殊對(duì)象屬性__interfaces。使用該屬性,JSweet允許instanceof在Java之類的接口上使用運(yùn)算符。
接口中的可選字段
接口可以定義可選字段,用于在程序員忘記初始化對(duì)象中的必填字段時(shí)報(bào)告錯(cuò)誤。在JSweet中支持可選字段是通過使用 @jsweet.lang.Optional注釋完成的。例如:
@Interface
public class Point {
public double x; public double y; @Optional public double z = 0;
}
在從接口構(gòu)造對(duì)象時(shí),JSweet編譯器將檢查字段是否已正確初始化。
// no errors (z is optional)
Point p1 = new Point() {{ x=1; y=1; }};
// JSweet reports a compile error since y is not optional
Point p2 = new Point() {{ x=1; z=1; }};
接口中的特殊JavaScript函數(shù)
在JavaScript中,對(duì)象可以具有屬性和函數(shù),但也可以(非排他地)用作構(gòu)造函數(shù)和函數(shù)本身。這在Java中是不可能的,因此JSweet定義了用于處理這些情況的特殊函數(shù)。
$apply 用于表示該對(duì)象可以用作函數(shù)。
$new 用于聲明該對(duì)象可以用作構(gòu)造函數(shù)。
例如,如果一個(gè)對(duì)象o是O定義 的接口$apply(),則寫:
o.$apply();
將轉(zhuǎn)變?yōu)椋?/p>
o();
同樣,如果O定義$new():
o.$new();
將轉(zhuǎn)變?yōu)椋?/p>
new o();
是的,它在Java中沒有意義,但在JavaScript中確實(shí)如此!
無類型對(duì)象(地圖)
在JavaScript中,對(duì)象可以看作包含鍵值對(duì)的映射(鍵通常稱為索引,尤其是當(dāng)它是數(shù)字時(shí))。因此,在JSweet中,所有對(duì)象都定義了特殊函數(shù)(定義于 def.js.Object):
$get(key) 使用給定鍵訪問值。
$set(key,value) 設(shè)置或替換給定鍵的值。
$delete(key) 刪除給定鍵的值。
反射/無類型訪問
的功能$get(key),$set(key,value)并且$delete(key)可以被看作是一個(gè)簡(jiǎn)單的反射API來訪問對(duì)象字段和狀態(tài)。還要注意靜態(tài)方法def.js.Object.keys(object),它返回給定對(duì)象上定義的所有鍵。
以下代碼使用此API來內(nèi)省對(duì)象的狀態(tài) o。
for(String key : def.js.Object.keys(o)) {
console.log("key=" + key + " value=" + o.$get(key));
});
當(dāng)沒有給定對(duì)象的類型化API時(shí),此API可用于以無類型方式操作對(duì)象(當(dāng)然應(yīng)盡可能避免使用)。
無類型對(duì)象初始化
可以使用該$set(key,value)函數(shù)創(chuàng)建新的無類型對(duì)象。例如:
Object point = new def.js.Object() {{ $set("x", 1); $set("y", 1); }};
它也轉(zhuǎn)化為:
var point = { "x": 1, "y": 1};
作為一種快捷方式,可以使用該jsweet.util.Lang.$map函數(shù),該函數(shù)轉(zhuǎn)換為完全相同的JavaScript代碼:
import static jsweet.util.Lang.$map;
[...]
Object point = $map("x", 1, "y", 1);
索引對(duì)象
可以為每個(gè)對(duì)象重載鍵和值的類型。例如,Array
對(duì)于使用數(shù)字鍵索引的對(duì)象,允許實(shí)現(xiàn)java.lang.Iterable接口,以便可以在foreach循環(huán)中使用它們。例如,NodeList類型(來自DOM)定義了一個(gè)索引函數(shù):
@Interface
class NodeList implements java.lang.Iterable {
public double length; public Node item(double index); public Node $get(double index);
}
在JSweet中,您可以使用該$get 函數(shù)訪問節(jié)點(diǎn)列表元素,也可以使用foreach語(yǔ)法進(jìn)行迭代。以下代碼生成完全有效的JavaScript代碼。
NodeList nodes = ...
for (int i = 0; i < nodes.length; i++) {
HTMLElement element = (HTMLElement) nodes.$get(i); [...]
}
// same as:
NodeList nodes = ...
for (Node node : nodes) {
HTMLElement element = (HTMLElement) node; [...]
}
枚舉
JSweet允許類似于Java定義枚舉。下面的代碼聲明與樹可能值的枚舉(A,B,和C)。
enum MyEnum {
A, B, C
}
以下語(yǔ)句是JSweet中的有效語(yǔ)句。
MyEnum e = MyEnum.A;
assert MyEnum.A == e;
assert e.name() == "A";
assert e.ordinal() == 0;
assert MyEnum.valueOf("A") == e;
assert array(MyEnum.values()).indexOf(MyEnum.valueOf("C")) == 2;
與Java枚舉一樣,可以在枚舉中添加其他方法,構(gòu)造函數(shù)和字段。
enum ScreenRatio {
FREE_RATIO(null), RATIO_4_3(4f / 3), RATIO_3_2(1.5f), RATIO_16_9(16f / 9), RATIO_2_1(2f / 1f), SQUARE_RATIO(1f); private final Float value; private MyComplexEnum(Float value) { this.value = value; } public Float getValue() { return value; }
}
枚舉便攜性說明
簡(jiǎn)單的枚舉被轉(zhuǎn)換為常規(guī)的TypeScript枚舉,即數(shù)字。在JavaScript中,在運(yùn)行時(shí),枚舉實(shí)例是簡(jiǎn)單編碼的序數(shù)。因此,JSweet枚舉很容易與TypeScript枚舉共享,即使使用枚舉,JSweet程序也可以與TypeScript程序進(jìn)行互操作。
具有其他成員的枚舉也會(huì)映射到TypeScript枚舉,但會(huì)生成另一個(gè)類來存儲(chǔ)其他信息。與TypeScript互操作時(shí),序號(hào)將保留,但附加信息將丟失。想要與TypeScript共享枚舉的程序員應(yīng)該知道這種行為。
全局
在Java中,與JavaScript相反,沒有全局變量或函數(shù)(只有靜態(tài)成員,但即使那些必須屬于一個(gè)類)。因此,JSweet引入了保留的Globals 類和globals包。這有兩個(gè)目的:
生成具有全局變量和函數(shù)的代碼(在Java中不鼓勵(lì)這樣做)
綁定到定義全局變量和函數(shù)的現(xiàn)有JavaScript代碼(盡可能多的JavaScript框架)
在Globals類中,只允許使用靜態(tài)字段(全局變量)和靜態(tài)方法(全局函數(shù))。以下是適用于Globals類的主要約束:
沒有非靜態(tài)成員
沒有超級(jí)課程
不能延長(zhǎng)
不能用作常規(guī)類的類型
沒有公共構(gòu)造函數(shù)(空私有構(gòu)造函數(shù)可以)
不能在方法中使用$ get,$ set和$ delete
例如,以下代碼片段將引發(fā)轉(zhuǎn)換錯(cuò)誤。
class Globals {
public int a; // error: public constructors are not allowed public Globals() { this.a = 3; } public static void test() { // error: no instance is available $delete("key"); }
}
// error: Globals classes cannot be used as types
Globals myVariable = null;
必須記住,Globals類和global包在運(yùn)行時(shí)被擦除,以便可以直接訪問它們的成員。例如mypackage.Globals.m(),在JSweet程序中,對(duì)應(yīng)mypackage.m()于生成的代碼中的函數(shù)以及運(yùn)行時(shí)的JavaScript VM中的 函數(shù)。此外,mypackage.globals.Globals.m()對(duì)應(yīng)于m()。
為了擦除生成代碼中的包,程序員也可以使用@Root注釋,這將在第5節(jié)中解釋 。
可選參數(shù)和重載
In JavaScript, parameters can be optional, in the sense that a parameter value does not need to be provided when calling a function. Except for varargs, which are fully supported in JSweet, the general concept of an optional parameter does not exist in Java. To simulate optional parameters, JSweet programmers can use method overloading, which is supported in Java. Here are some examples of supported overloads in JSweet:
String m(String s, double n) { return s + n; }
// simple overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
// complex overloading (JSweet generates more complex code to mimic the Java behavior)
String m(String s) { return s; }
Bridging to external JavaScript elements
It can be the case that programmers need to use existing libraries from JSweet. In most cases, one should look up in the available candies, a.k.a. bridges at http://www.jsweet.org/jsweet-... When the candy does not exist, or does not entirely cover what is needed, one can create new definitions in the program just by placing them in the def.libname package. Definitions only specify the types of external libraries, but no implementations. Definitions are similar to TypeScript’s .d.ts definition files (actually JSweet generates intermediate TypeScript definition files for compilation purposes). Definitions can also be seen as similar to .h C/C++ header files.
Examples
以下示例顯示了使用簡(jiǎn)單定義可供JSweet程序員訪問的主干存儲(chǔ)類。此類僅用于鍵入,將作為TypeScript定義生成,并在JavaScript生成期間擦除。
package def.backbone;
class Store {
public Store(String dbName) {}
}
請(qǐng)注意,定義類構(gòu)造函數(shù)必須具有空體。此外,定義類方法必須是native。例如:
package def.mylib;
class MyExternalJavaScriptClass {
public native myExternalJavaScriptMethod();
}
可以在定義中定義屬性,但是,無法初始化這些屬性。
寫定義規(guī)則(又名橋梁)
按照慣例,將類放在def.libname包中定義了一組libname名為的外部JavaScript庫(kù) 的定義libname。請(qǐng)注意,此機(jī)制類似于TypeScript d.ts 定義文件。
Candies(外部JavaScript庫(kù)的橋梁)使用定義。例如,jQuery candy定義了def.jquery包中的所有jQuery API 。
以下是編寫定義時(shí)需要遵循的規(guī)則和約束的列表。
接口比類更受歡迎,因?yàn)榻涌诳梢院喜ⅲ惪梢詫?shí)例化。僅當(dāng)API定義顯式構(gòu)造函數(shù)(可以使用其創(chuàng)建對(duì)象new)時(shí),才應(yīng)使用類。要在JSweet中定義接口,只需使用注釋類@jsweet.lang.Interface。
必須將頂級(jí)函數(shù)和變量定義為類中的public static 成員Globals。
所有類,接口和包都應(yīng)記錄在Javadoc標(biāo)準(zhǔn)之后的注釋。
當(dāng)函數(shù)參數(shù)有多種類型時(shí),方法重載應(yīng)優(yōu)先于使用union類型。當(dāng)無法進(jìn)行方法重載時(shí),簡(jiǎn)單地使用Object類型會(huì)更方便。鍵入的強(qiáng)度較低,但更容易使用。
可以使用字符串類型來提供函數(shù)重載,具體取決于字符串參數(shù)值。
在方法簽名中,可以使用@jsweet.lang.Optional注釋定義可選參數(shù) 。
在界面中,可以使用@jsweet.lang.Optional注釋定義可選字段 。
定義可以直接嵌入到JSweet項(xiàng)目中,以便以類型化的方式訪問外部庫(kù)。
定義也可以打包成糖果(Maven工件),以便它們可以被其他項(xiàng)目共享。有關(guān)如何創(chuàng)建糖果的完整詳細(xì)信息,請(qǐng)參閱“ 包裝”部分。請(qǐng)注意,在使用JSweet編寫庫(kù)時(shí)不需要編寫定義,因?yàn)镴ava API可以直接訪問,并且可以使用該declaration選項(xiàng)由JSweet自動(dòng)生成TypeScript定義。
無法訪問
有時(shí),定義不可用或不正確,只需要一個(gè)小補(bǔ)丁即可訪問功能。程序員必須記住,JSweet只是一個(gè)語(yǔ)法層,并且總是可以繞過鍵入以訪問未在API中明確指定的字段或函數(shù)。
雖然具有良好類型的API是首選和建議的方式,但是當(dāng)這樣的API不可用時(shí),使用def.js.Object.$get允許反射訪問方法和屬性,然后可以將其轉(zhuǎn)換為正確的類型。為了以無類型方式訪問函數(shù),可以強(qiáng)制轉(zhuǎn)換 def.js.Function并調(diào)用泛型和非類型化方法$apply。例如,以下是$在jQuery API不可用時(shí)如何調(diào)用jQuery 方法:
import def.dom.Globals.window;
[...]
Function $ = (Function)window.$get("$");
$.$apply("aCssSelector"):
該$get函數(shù)可用于def.js.Object(或子類)的實(shí)例。對(duì)于a def.js.Object,您可以使用jsweet.util.Lang.object輔助方法強(qiáng)制轉(zhuǎn)換它 。例如:
import static jsweet.dom.Lang.object;
[...]
object(anyObject).$get("$");
最后,jsweet.util.Lang.$inserthelper方法允許用戶在程序中插入任何TypeScript表達(dá)式。無效的表達(dá)式將引發(fā)TypeScript編譯錯(cuò)誤,但不建議使用此技術(shù)。
import static jsweet.dom.Lang.$get;
import static jsweet.dom.Lang.$apply;
[...]
// generate anyObject"prop";
$apply($get(anyObject, "prop"), "param");
最后,還要注意使用jsweet.util.Lang.any輔助方法,這對(duì)于擦除鍵入非常有用。由于該any方法any在TypeScript中生成類型的強(qiáng)制轉(zhuǎn)換,因此它比例如強(qiáng)制轉(zhuǎn)換更激進(jìn)Object。以下示例說明如何使用該any方法將Int32ArrayJava轉(zhuǎn)換為Java int[](然后允許對(duì)其進(jìn)行直接索引訪問)。
ArrayBuffer arb = new ArrayBuffer(2 2 4);
int[] array = any(new Int32Array(arb));
int whatever = array[0];
混入
在JavaScript中,通常的做法是使用新聞元素(字段和方法)來增強(qiáng)現(xiàn)有類。它是框架定義插件時(shí)使用的擴(kuò)展機(jī)制。通常,jQuery插件會(huì)向JQuery類中添加新元素。例如,jQuery計(jì)時(shí)器插件timer向JQuery該類添加一個(gè)字段。因此,JQuery如果您多帶帶使用jQuery,或者使用其計(jì)時(shí)器插件增強(qiáng)jQuery ,則 該類沒有相同的原型。
在Java中,此擴(kuò)展機(jī)制存在問題,因?yàn)镴ava語(yǔ)言默認(rèn)情況下不支持mixins或任何類型的擴(kuò)展。
無法訪問mixins
程序員可以使用訪問$get器和/或強(qiáng)力轉(zhuǎn)換來訪問添加的元素。
以下是$get用于計(jì)時(shí)器插件的示例:
((Timer)$("#myId").$get("timer")).pause();
這是另一種通過使用jQuery UI插件來實(shí)現(xiàn)它的方法(請(qǐng)注意,此解決方案強(qiáng)制使用def.jqueryui.JQuery 而不是def.jquery.JQuery為了訪問menu()由UI插件添加的功能):
import def.jqueryui.JQuery;
[...]
Object obj = $("#myMenu");
JQuery jq = (JQuery) obj;
jq.menu();
然而,這些解決方案并不完全令人滿意,因?yàn)樵诖蜃址矫婷黠@不安全。
使用mixins鍵入訪問
當(dāng)需要交叉糖果動(dòng)態(tài)擴(kuò)展時(shí),JSweet定義了mixin的概念。mixin是一個(gè)定義成員的類,最終可以在目標(biāo)類(mixin-ed類)中直接訪問。Mixins使用@Mixin注釋定義。這是def.jqueryui.JQuerymixin 的摘錄 :
package def.jqueryui;
import def.dom.MouseEvent;
import def.js.Function;
import def.js.Date;
import def.js.Array;
import def.js.RegExp;
import def.dom.Element;
import def.jquery.JQueryEventObject;
@jsweet.lang.Interface
@jsweet.lang.Mixin(target=def.jquery.JQuery.class)
public abstract class JQuery extends def.jquery.JQuery {
native public JQuery accordion(); native public void accordion(jsweet.util.StringTypes.destroy methodName); native public void accordion(jsweet.util.StringTypes.disable methodName); native public void accordion(jsweet.util.StringTypes.enable methodName); native public void accordion(jsweet.util.StringTypes.refresh methodName); ... native public def.jqueryui.JQuery menu(); ...
人們可以注意到@jsweet.lang.Mixin(target=def.jquery.JQuery.class) ,這個(gè)mixin將被合并到一起,def.jquery.JQuery以便用戶能夠直接以一種良好的方式使用所有UI插件成員。
如何使用
TBD。
從現(xiàn)有的TypeScript定義生成JSweet糖果
TBD。
輔助類型
JSweet使用大多數(shù)Java輸入功能(包括功能類型),但也使用所謂的輔助類型擴(kuò)展Java類型系統(tǒng)。輔助類型背后的想法是創(chuàng)建可以通過使用類型參數(shù)(也稱為泛型)來保存鍵入信息的類或接口 ,以便JSweet轉(zhuǎn)換器可以涵蓋更多的鍵入方案。這些類型已經(jīng)從TypeScript類型系統(tǒng)映射,它比Java更豐富(主要是因?yàn)镴avaScript是一種動(dòng)態(tài)語(yǔ)言,需要比Java更多的打字場(chǎng)景)。
功能類型
用于功能類型,JSweet重用java.Runnable和 java.util.function爪哇8的功能接口,這些接口是通用的,但只支持高達(dá)2參數(shù)的功能。因此,JSweet為更多參數(shù)添加了一些支持jsweet.util.function,因?yàn)樗荍avaScript API中的常見情況。
以下是使用Function通用功能類型的示例:
import java.util.function.Function;
public class C {
String test(Functionf) { f.apply("a"); } public static void main(String[] args) { String s = new C().test(p -> p); assert s == "a"; }
}
我們鼓勵(lì)程序員使用jsweet.util.function和java.util.function(以及 java.lang.Runnable)中定義的通用功能接口。當(dāng)需要具有更多參數(shù)的函數(shù)時(shí),程序員可以jsweet.util.function通過遵循與現(xiàn)有函數(shù)相同的模板來定義他們自己的通用函數(shù)類型 。
在某些情況下,程序員更愿意定義自己的特定功能接口。這得到了JSweet的支持。例如:
@FunctionalInterface
interface MyFunction {
void run(int i, String s);
}
public class C {
void m(MyFunction f) { f.run(1, "test"); } public static void main(String[] args) { new C().m((i, s) -> { // do something with i and s }); }
}
重要警告:這里要注意的是,與Java相反,@FunctionInterface注釋的使用是強(qiáng)制性的。
還要注意apply函數(shù)的可能用途,按照慣例,該函數(shù)始終是目標(biāo)對(duì)象的功能定義(除非使用apply注釋進(jìn)行@Name注釋)。定義/調(diào)用 apply可以在任何類/對(duì)象上完成(因?yàn)樵贘avaScript中任何對(duì)象都可以成為一個(gè)功能對(duì)象)。
對(duì)象類型
對(duì)象類型與接口類似:它們定義了一組適用于對(duì)象的字段和方法(但請(qǐng)記住它是一個(gè)編譯時(shí)合同)。在TypeScript中,對(duì)象類型是內(nèi)聯(lián)的和匿名的。例如,在TypeScript中,以下方法m接受一個(gè)參數(shù),該參數(shù)是包含index字段的對(duì)象:
// TypeScript:
public class C {
public m(param : { index : number }) { ... }
}
對(duì)象類型是編寫較短代碼的便捷方式。可以通過動(dòng)態(tài)構(gòu)建對(duì)象來傳遞正確鍵入的對(duì)象:
// TypeScript:
var c : C = ...;
c.m({ index : 2 });
顯然,對(duì)象類型是一種使程序員很容易輸入JavaScript程序的方法,這是TypeScript的主要目標(biāo)之一。它使得JavaScript程序員的輸入簡(jiǎn)潔,直觀,直觀。在Java / JSweet中,不存在類似的內(nèi)聯(lián)類型,Java程序員用于為這種情況定義類或接口。因此,在JSweet中,程序員必須定義用@ObjectType對(duì)象類型注釋的輔助類。這可能看起來更復(fù)雜,但它有利于強(qiáng)制程序員命名所有類型,最終可以根據(jù)上下文導(dǎo)致更易讀和可維護(hù)的代碼。請(qǐng)注意,與接口類似,對(duì)象類型在運(yùn)行時(shí)被擦除。另外@ObjectType 注解的類可以內(nèi)部類,使他們?cè)诒镜厥褂谩?/p>
這是以前的TypeScript程序的JSweet版本。
public class C {
@ObjectType public static class Indexed { int index; } public void m(Indexed param) { ... }
}
使用對(duì)象類型與使用接口類似:
C c = ...;
c.m(new Indexed() {{ index = 2; }});
當(dāng)對(duì)象類型是共享對(duì)象并表示可以在多個(gè)上下文中使用的鍵入實(shí)體時(shí),建議使用 @Interface注釋而不是@ObjectType。這是基于界面的版本。
@Interface
public class Indexed {
int index;
}
public class C {
public m(Indexed param) { ... }
}
C c = ...;
c.m(new Indexed {{ index = 2; }});
字符串類型
在TypeScript中,字符串類型是一種根據(jù)字符串參數(shù)的值來模擬函數(shù)重載的方法。例如,這是DOM TypeScript定義文件的簡(jiǎn)化摘錄:
// TypeScript:
interface Document {
[...] getElementsByTagName(tagname: "a"): NodeListOf; getElementsByTagName(tagname: "b"): NodeListOf ; getElementsByTagName(tagname: "body"): NodeListOf ; getElementsByTagName(tagname: "button"): NodeListOf ; [...]
}
在此代碼中,getElementsByTagName函數(shù)都是依賴于傳遞給tagname參數(shù)的字符串的重載。不僅字符串類型允許函數(shù)重載(通常在TypeScript / JavaScript中不允許),但它們也約束字符串值(類似于枚舉),因此編譯器可以自動(dòng)檢測(cè)字符串值中的拼寫錯(cuò)誤并引發(fā)錯(cuò)誤。
此功能對(duì)代碼質(zhì)量很有用,JSweet提供了一種機(jī)制來模擬具有相同級(jí)別類型安全性的字符串類型。字符串類型是使用注釋的公共靜態(tài)字段@StringType。必須使用在同一容器類型中聲明的同名接口鍵入它。
對(duì)于JSweet翻譯庫(kù)(糖果),所有字符串類型都在類中聲明jsweet.util.StringTypes,因此程序員很容易找到它們。舉例來說,如果一個(gè)"body"字符串類型需要定義,一個(gè)名為Java接口body和一個(gè)靜態(tài)的最終場(chǎng)被稱為body在一個(gè)定義jsweet.util.StringTypes。
請(qǐng)注意,每個(gè)糖果可能在jsweet.util.StringTypes類中定義了自己的字符串類型 。JSweet轉(zhuǎn)換器在字節(jié)碼級(jí)別合并所有這些類,以便所有糖果的所有字符串類型在同一個(gè)jsweet.util.StringTypes實(shí)用程序類中可用。因此,JSweet DOM API將如下所示:
@Interface
public class Document {
[...] public native NodeListOfgetElementsByTagName(a tagname); public native NodeListOf getElementsByTagName(b tagname); public native NodeListOf getElementsByTagName(body tagname); public native NodeListOf getElementsByTagName(button tagname); [...]
}
在此API中a,b,body和button是在定義的接口 jsweet.util.StringTypes類。當(dāng)使用一種方法時(shí) Document,程序員只需要使用相應(yīng)的類型實(shí)例(同名)。例如:
Document doc = ...;
NodeListOf
注意:如果字符串值不是有效的Java標(biāo)識(shí)符(例如 "2d"或者"string-with-dashes"),則將其轉(zhuǎn)換為有效的標(biāo)識(shí)符并使??用注釋@Name("originalName"),以便JSweet轉(zhuǎn)換器知道必須在生成的代碼中使用什么實(shí)際字符串值。例如,默認(rèn)情況下,"2d"和"string-with-dashes"將對(duì)應(yīng)于接口StringTypes._2d和 StringTypes.string_with_dashes與@Name注解。
程序員可以根據(jù)自己的需要定義字符串類型,如下所示:
import jsweet.lang.Erased;
import jsweet.lang.StringType;
public class CustomStringTypes {
@Erased public interface abc {} @StringType public static final abc abc = null; // This method takes a string type parameter void m2(abc arg) { } public static void main(String[] args) { new CustomStringTypes().m2(abc); }
}
注意使用@Erased注釋,它允許聲明abc內(nèi)部接口。此接口用于鍵入字符串類型字段abc。通常,我們建議程序員將程序的所有字符串類型分組到同一個(gè)實(shí)用程序類中,以便于查找它們。
元組類型
元組類型表示具有多帶帶跟蹤的元素類型的JavaScript數(shù)組。對(duì)于的元組類型,JSweet定義參數(shù)化輔助類TupleN
例如,給定以下大小為2的元組:
Tuple2
我們可以期待以下(良好類型)行為:
assert tuple.$0 == "test";
assert tuple.$1 == 10;
tuple.$0 = "ok";
tuple.$1--;
assert tuple.$0 == "ok";
assert tuple.$1 == 9;
元組類型都在jsweet.util.tuple包中定義(并且必須定義) 。默認(rèn)情況下Tuple[2..6],定義了類。當(dāng)在糖果API中遇到時(shí),會(huì)自動(dòng)生成其他元組(> 6)。當(dāng)然,當(dāng)需要在jsweet.util.tuple包中找不到更大的元組時(shí),程序員可以根據(jù)需要在該包中添加自己的元組,只需遵循與現(xiàn)有元組相同的模板即可。
聯(lián)盟類型
聯(lián)合類型表示可能具有多個(gè)不同表示之一的值。當(dāng)這種情況發(fā)生在方法簽名中時(shí)(例如,允許給定參數(shù)的幾種類型的方法),JSweet利用了Java中可用的方法重載機(jī)制。例如,以下m方法接受參數(shù)p,該參數(shù)可以是a String或a Integer。
public void m(String p) {...}
public void m(Integer p) {...}
在前一種情況下,不需要使用顯式聯(lián)合類型。對(duì)于更一般的情況,JSweet 在 包中定義了一個(gè)輔助接口 Union
以下代碼顯示了JSweet中union類型的典型用法。它只是將一個(gè)變量聲明為一個(gè)字符串和一個(gè)數(shù)字之間的聯(lián)合,這意味著該變量實(shí)際上可以是其中一種類型(但沒有其他類型)。從聯(lián)合類型到常規(guī)類型的切換是通過jsweet.util.Lang.union輔助方法完成的。這個(gè)輔助方法是完全無類型的,允許從Java角度將任何聯(lián)合轉(zhuǎn)換為另一種類型。它實(shí)際上是JSweet轉(zhuǎn)換器,它檢查是否一直使用了union類型。
import static jsweet.util.Lang.union;
import jsweet.util.union.Union;
[...]
Union
// u can be used as a String
String s = union(u);
// or a number
Number n = union(u);
// but nothing else
Date d = union(u); // JSweet error
如果union需要,也可以使用其他方式將助手從常規(guī)類型切換回聯(lián)合類型。
import static jsweet.util.Lang.union;
import jsweet.util.union.Union3;
[...]
public void m(Union3
[...]
// u can be a String, a Number or a Date
m(union("a string"));
// but nothing else
m(union(new RegExp(".*"))); // compile error
注意:在鍵入函數(shù)參數(shù)時(shí),優(yōu)先使用Java函數(shù)重載而不是union類型。例如:
// with union types (discouraged)
native public void m(Union3
// with overloading (preferred way)
native public void m(String s);
native public void m(Number n);
native public void m(Date d);
交叉類型
TypeScript定義了類型交集的概念。當(dāng)類型相交時(shí),意味著結(jié)果類型是更大的類型,它是所有相交類型的總和。例如,在TypeScript中, A & B對(duì)應(yīng)于定義兩者A和B成員的類型。
由于許多原因,Java中的交集類型無法輕松實(shí)現(xiàn)。因此,這里做出的實(shí)際選擇是使用聯(lián)合類型代替交集類型。A & B因此,在JSweet中定義為 Union,這意味著程序員可以使用輔助方法訪問這兩者A和 B成員jsweet.util.Lang.union。它當(dāng)然不如TypeScript版本方便,但它仍然是類型安全的。
語(yǔ)義
語(yǔ)義指定給定程序在執(zhí)行時(shí)的行為方式。雖然JSweet依賴于Java語(yǔ)法,但程序被轉(zhuǎn)換為JavaScript并且不在JRE中運(yùn)行。因此,與Java程序相比,JavaScript語(yǔ)義將影響JSweet程序的最終語(yǔ)義。在本節(jié)中,我們將通過關(guān)注Java / JavaSript和JSweet之間的差異或共性來討論語(yǔ)義。
主要方法
主要方法是程序執(zhí)行入口點(diǎn),并且在main評(píng)估包含方法的類時(shí)將全局調(diào)用。例如:
public class C {
private int n; public static C instance; public static void main(String[] args) { instance = new C(); instance.n = 4; } public int getN() { return n; }
}
// when the source file containing C has been evaluated:
assert C.instance != null;
assert C.instance.getN() == 4;
全局調(diào)用main方法的方式取決于程序的打包方式。有關(guān)詳細(xì)信息,請(qǐng)參閱附錄。
初始化器
初始化器的行為與Java類似。
例如:
public class C1 {
int n; { n = 4; }
}
assert new C1().n == 4;
與靜態(tài)初始化器類似:
public class C2 {
static int n; static { n = 4; }
}
assert C2.n == 4;
雖然在實(shí)例化類時(shí)會(huì)評(píng)估常規(guī)初始值設(shè)定項(xiàng),但是為了避免前向依賴性問題而懶惰地評(píng)估靜態(tài)初始化程序,并模擬初始化程序的Java行為。使用JSweet,程序員可以定義靜態(tài)字段或靜態(tài)初始化程序,它依賴于尚未初始化的靜態(tài)字段。
有關(guān)此行為的更多詳細(xì)信息,請(qǐng)參閱附錄。
數(shù)組初始化和分配
數(shù)組可以像Java一樣使用。
String[] strings = { "a", "b", "c" };
assert strings[1] == "b";
指定維度時(shí),數(shù)組是預(yù)先分配的(如Java中所示),因此它們使用正確的長(zhǎng)度進(jìn)行初始化,并在多維數(shù)組的情況下使用正確的子數(shù)組進(jìn)行初始化。
String[][] strings = new String2;
assert strings.length == 2;
assert strings[0].length == 2;
strings0 = "a";
assert strings0 == "a";
通過強(qiáng)制轉(zhuǎn)換為def.js.Arraywith,可以在數(shù)組上使用JavaScript API jsweet.util.Lang.array。
import static jsweet.util.Lang.array;
[...]
String[] strings = { "a", "b", "c" };
assert strings.length == 3;
array(strings).push("d");
assert strings.length == 4;
assert strings[3] == "d";
在某些情況下,最好def.js.Array直接使用該類。
Array
// same as: Array
// same as: Array
assert strings.length == 3;
strings.push("d");
assert strings.length == 4;
assert strings.$get(3) == "d";
異步編程
在ES2015 + Promise API的幫助下,JSweet支持基本回調(diào)概念之外的高級(jí)異步編程。
承諾
通過聲明Promise返回類型來定義異步方法非常簡(jiǎn)單 。當(dāng)毫秒毫秒過去時(shí),Promise將 滿足以下方法。
Promise
return new Promise
setTimeout(resolve, millis);
});
}
然后,您可以在履行承諾后鏈接同步和異步操作。
delay(1000)
// chain with a synchronous action with "then". Here we just return a constant.
.then(() -> {
System.out.println("wait complete"); return 42;
})
// chain with an asynchronous action with "thenAsync". Here it is implied that anotherAsyncAction(String) returns a Promise<...>
.thenAsync((Integer result) -> {
System.out.println("previous task result: " + result); // will print "previous task result: 42" return anotherAsyncAction("param");
})
// this chained action will be executed once anotherAsyncAction finishes its execution.
.then((String result) -> {
System.out.println("anotherAsyncAction returned " + result);
})
// catch errors during process using this method
.Catch(error -> {
System.out.println("error is " + error);
});
這允許完全類型安全和流暢的異步編程模型。
異步/ AWAIT
Promises非常有趣,以避免回調(diào),但編寫它仍然需要大量的樣板代碼。它比純回調(diào)更好,但比線性編程更不易讀和直接。這就是async/await幫助的地方 。
使用await關(guān)鍵字,您可以告訴運(yùn)行時(shí)等待 Promise實(shí)現(xiàn),而無需編寫then方法。await“是” then部分之后的代碼。結(jié)果是您可以使用線性編程編寫異步代碼。
import static jsweet.util.Lang.await;
// wait for the Promise returned by the delay method to be fulfilled
await(delay(1000));
System.out.println("wait complete");
錯(cuò)誤處理也是如此。您可以使用普通的 try / catch習(xí)語(yǔ)來處理異常。
import static jsweet.util.Lang.await;
import def.js.Error;
try {
Integer promiseResult = await(getANumber());
assert promiseResult == 42;
} catch(Error e) {
System.err.println("something unexpected happened: " + e);
}
你必須聲明為async每個(gè)異步方法/ lambda(即每個(gè)等待某事的方法)。
import static jsweet.util.Lang.await;
import static jsweet.util.Lang.async;
import static jsweet.util.Lang.function;
import jsweet.lang.Async;
import def.js.Function;
@Async
Promise
await(delay(1000)); // won"t compile if the enclosing method isn"t @Async
return asyncReturn(42); // converts to Promise
}
@Async
void askAnswerThenVerifyAndPrintIt() {
try {
Integer answer = await(findAnswer()); // lambda expressions can be async Function verifyAnswerAsync = async(function(() -> { return await(answerService.verifyAnswer(answer)); })) Boolean verified = await(verifyAnswerAsync.$apply()); if (!verified) { throw new Error("cannot verify this answer"); } console.log("answer found: " + answer);
} catch (Error e) {
console.error(e, "asynchronous process failed");
}
}
甜,不是嗎?;)
姓名沖突
與TypeScript / JavaScript相反,Java在方法,字段和包之間存在根本區(qū)別。Java還支持方法重載(具有相同名稱的不同簽名的方法)。在JavaScript中,對(duì)象變量和函數(shù)存儲(chǔ)在同一個(gè)對(duì)象映射中,這基本上意味著您不能為多個(gè)對(duì)象成員使用相同的密鑰(這也解釋了Java中無法實(shí)現(xiàn)Java語(yǔ)義中的方法重載)。因此,在TypeScript中生成時(shí),某些Java代碼可能包含名稱沖突。JSweet將盡可能自動(dòng)避免名稱沖突,并在其他情況下報(bào)告聲音錯(cuò)誤。
方法和字段名稱沖突
JSweet執(zhí)行轉(zhuǎn)換以自動(dòng)允許方法和私有字段具有相同的名稱。另一方面,同一個(gè)類或具有子類鏈接的類中不允許使用相同名稱的方法和公共字段。
為了避免由于這種JavaScript行為導(dǎo)致編程錯(cuò)誤,JSweet添加了一個(gè)語(yǔ)義檢查來檢測(cè)類中的重復(fù)名稱(這也考慮了在父類中定義的成員)。舉個(gè)例子:
public class NameClashes {
// error: field name clashes with existing method name public String a; // error: method name clashes with existing field name public void a() { return a; }
}
方法重載
與TypeScript和JavaScript相反(但與Java類似),JSweet中可能有多個(gè)具有相同名稱但具有不同參數(shù)的方法(所謂的重載)。我們區(qū)分了簡(jiǎn)單的重載和復(fù)雜的重載。簡(jiǎn)單重載是使用方法重載來定義可選參數(shù)。JSweet允許這個(gè)習(xí)慣用語(yǔ),它對(duì)應(yīng)于以下模板:
String m(String s, double n) { return s + n; }
// valid overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
在這種情況下,JSweet將生成JavaScript代碼,只有一個(gè)方法具有可選參數(shù)的默認(rèn)值,因此生成的程序的行為對(duì)應(yīng)于原始程序。在這種情況下:
function m(s, n = 0) { return s + n; }
如果程序員嘗試以不同方式使用重載,例如通過為同一方法名定義兩個(gè)不同的實(shí)現(xiàn),JSweet將回退復(fù)雜的重載,包括生成根實(shí)現(xiàn)(包含更多參數(shù)的方法)和一個(gè)輔助實(shí)現(xiàn)per overloading方法(用表示方法簽名的后綴命名)。根實(shí)現(xiàn)是通用的,并通過測(cè)試給定參數(shù)的值和類型調(diào)度到其他實(shí)現(xiàn)。例如:
String m(String s, double n) { return s + n; }
String m(String s) { return s; }
生成以下(略微簡(jiǎn)化的)JavaScript代碼:
function m(s, n) {
if(typeof s === "string" && typeof n === "number") { return s + n; } else if(typeof s === "string" && n === undefined) { return this.m$java_lang_String(s); } else { throw new Error("invalid overload"); }
}
function m$java_lang_String(s) { return s; }
局部變量名稱
在TypeScript / JavaScript中,局部變量可能與使用全局方法沖突。例如,使用alertDOM(jsweet.dom.Globals.alert)中的全局方法要求沒有局部變量隱藏它:
import static jsweet.dom.Globals.alert;
[...]
public void m1(boolean alert) {
// JSweet compile error: name clash between parameter and method call alert("test");
}
public void m2() {
// JSweet compile error: name clash between local variable and method call String alert = "test"; alert(alert);
}
請(qǐng)注意,在調(diào)用全局方法時(shí)使用完全限定名稱時(shí)也會(huì)發(fā)生此問題(這是因?yàn)橄薅l件在TypeScript / JavaScript中被刪除)。在任何情況下,JSweet都會(huì)在發(fā)生此類問題時(shí)報(bào)告聲音錯(cuò)誤,以便程序員可以調(diào)整局部變量名稱以避免與全局變量發(fā)生沖突。
測(cè)試對(duì)象的類型
要在運(yùn)行時(shí)測(cè)試給定對(duì)象的類型,可以使用instanceofJava運(yùn)算符,也可以使用 Object.getClass()函數(shù)。
instanceof
這instanceof是在運(yùn)行時(shí)測(cè)試類型的建議和首選方法。JSweet將transpile到常規(guī)instanceof或一個(gè) typeof取決于所測(cè)試的類型的操作(這將在回退 typeof對(duì)number,string和boolean核心類型)。
盡管不是必需的,但也可以使用實(shí)用方法直接使用typeof JSweet中的運(yùn)算符jsweet.util.Lang.typeof。以下是有效類型測(cè)試的一些示例:
import static jsweet.util.Lang.typeof;
import static jsweet.util.Lang.equalsStrict;
[...]
Number n1 = 2;
Object n2 = 2;
int n3 = 2;
Object s = "test";
MyClass c = new MyClass();
assert n1 instanceof Number; // transpiles to a typeof
assert n2 instanceof Number; // transpiles to a typeof
assert n2 instanceof Integer; // transpiles to a typeof
assert !(n2 instanceof String); // transpiles to a typeof
assert s instanceof String; // transpiles to a typeof
assert !(s instanceof Integer); // transpiles to a typeof
assert c instanceof MyClass;
assert typeof(n3) == "number";
從JSweet版本1.1.0開始,instanceof接口上也允許運(yùn)算符,因?yàn)镴Sweet會(huì)跟蹤所有對(duì)象的所有實(shí)現(xiàn)接口。通過調(diào)用的對(duì)象中的附加隱藏屬性來確保此接口跟蹤,__interfaces并且包含對(duì)象實(shí)現(xiàn)的所有接口的名稱(直接或通過在編譯時(shí)確定的類繼承樹)。因此,如果instanceof 運(yùn)算符的類型參數(shù)是接口,JSweet只是檢查對(duì)象的 __interfaces字段是否存在并包含給定的接口。例如,當(dāng)Point接口是這個(gè)代碼在JSweet中完全有效:
Point p1 = new Point() {{ x=1; y=1; }};
[...]
assert p1 instanceof Point
Object.getClass() 和 X.class
在JSweet中,可以使用Object.getClass()on any實(shí)例。它實(shí)際上將返回類的構(gòu)造函數(shù)。X.class如果X是類,using 也將返回構(gòu)造函數(shù)。因此,以下斷言將在JSweet中保留:
String s = "abc";
assert String.class == s.getClass()
在課堂上,您可以調(diào)用getSimpleName()或getName()函數(shù)。
String s = "abc";
assert "String" == s.getClass().getSimpleName()
assert String.class.getSimpleName() == s.getClass().getSimpleName()
請(qǐng)注意,getSimpleName()或者getName()函數(shù)也適用于接口。但是,您必須知道,X.class如果X是接口,將以字符串形式編碼(保存接口的名稱)。
限制和約束
由于所有數(shù)字都映射到JavaScript數(shù)字,因此JSweet不區(qū)分整數(shù)和浮點(diǎn)數(shù)。因此, 無論實(shí)際類型是什么n instanceof Integer,n instanceof Float都會(huì)給出相同的結(jié)果n。對(duì)于字符串和字符存在相同的限制,這些字符串和字符在運(yùn)行時(shí)無法區(qū)分,但也適用于具有相同參數(shù)數(shù)量的函數(shù)。例如,一個(gè)實(shí)例IntFunction
這些限制對(duì)函數(shù)重載有直接影響,因?yàn)橹剌d使用instanceof運(yùn)算符來決定調(diào)用哪個(gè)重載。
就像在JavaScript中工作時(shí)一樣,序列化對(duì)象必須與其實(shí)際類正確“復(fù)活”,以便 instanceof操作員可以再次工作。例如,通過創(chuàng)建的點(diǎn)對(duì)象Point p = (Point)JSON.parse("{x:1,y:1}")不會(huì)對(duì)instanceof運(yùn)算符起作用。如果您遇到這樣的用例,您可以聯(lián)系我們獲取一些有用的JSweet代碼以正確恢復(fù)對(duì)象類型。
lambda表達(dá)式中的變量作用域
已知JavaScript變量作用域給程序員帶來了一些問題,因?yàn)榭梢詮氖褂么俗兞康膌ambda外部更改對(duì)變量的引用。因此,JavaScript程序員不能依賴于在lambda范圍之外聲明的變量,因?yàn)楫?dāng)執(zhí)行l(wèi)ambda時(shí),該變量可能已在程序中的其他位置被修改。例如,以下程序顯示了一個(gè)典型案例:
NodeList nodes = document.querySelectorAll(".control");
for (int i = 0; i < nodes.length; i++) {
HTMLElement element = (HTMLElement) nodes.$get(i); // final element.addEventListener("keyup", (evt) -> { // this element variable will not change here element.classList.add("hit"); });
}
在JavaScript中(注意EcmaScript 6修復(fù)了這個(gè)問題),這樣的程序?qū)o法實(shí)現(xiàn)其目的,因?yàn)閑lement事件監(jiān)聽器中使用的變量被for循環(huán)修改并且不保持期望值。在JSweet中,這些問題與最終的Java變量類似。在我們的示例中,element變量在lambda表達(dá)式中重新定義,以便封閉循環(huán)不會(huì)更改其值,因此程序的行為與Java類似(正如大多數(shù)程序員所預(yù)期的那樣)。
這個(gè)范圍
與JavaScript相反,與Java類似,將方法用作lambda將防止丟失對(duì)引用的引用this。例如,在action以下程序的方法中this,即使在方法中action被稱為lambda 時(shí),也保持正確的值main。雖然這對(duì)Java程序員來說似乎是合乎邏輯的,但JavaScript語(yǔ)義并不能確保這種行為。
package example;
import static jsweet.dom.Globals.console;
public class Example {
private int i = 8; public Runnable getAction() { return this::action; } public void action() { console.log(this.i); // this.i is 8 } public static void main(String[] args) { Example instance = new Example(); instance.getAction().run(); }
}
重要的是要強(qiáng)調(diào)this通過與ES5 bind功能類似的機(jī)制確保正確的值。結(jié)果是函數(shù)引用被包裝在函數(shù)中,這意味著函數(shù)指針(例如this::action)動(dòng)態(tài)地創(chuàng)建包裝函數(shù)。它在操作函數(shù)指針時(shí)有副作用,這個(gè)問題在本期https://github.com/cincheo/js... 。
打包
打包是JavaScript的復(fù)雜點(diǎn)之一,尤其是來自Java時(shí)。JavaScript包裝的復(fù)雜性歸結(jié)為JavaScript本身沒有定義任何包裝這一事實(shí)。因此,多年來出現(xiàn)了許多事實(shí)上的解決方案和指南,使得對(duì)常規(guī)Java程序員的包裝理解不安。JSweet提供了有用的選項(xiàng)并生成代碼,以便通過使包裝問題更加透明和大多數(shù)情況下的Java“簡(jiǎn)單”來簡(jiǎn)化Java程序員的生活。在本節(jié)中,我們將描述和解釋典型的包裝方案。
使用您的文件,無需任何包裝
運(yùn)行程序最常見和最簡(jiǎn)單的情況就是將每個(gè)生成的文件包含在HTML頁(yè)面中。這不是任何包裝選項(xiàng)的默認(rèn)模式。例如,當(dāng)您的程序定義兩個(gè)類x.y.z.A并x.y.z.B在兩個(gè)多帶帶的文件中時(shí),您可以按如下方式使用它們:
[...]
這樣做時(shí),程序員需要非常謹(jǐn)慎,以避免文件之間的正向靜態(tài)依賴關(guān)系。換句話說,A 類不能B在靜態(tài)字段,靜態(tài)初始化程序或靜態(tài)導(dǎo)入中使用任何內(nèi)容,否則在嘗試加載頁(yè)面時(shí)會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。此外,A該類不能擴(kuò)展B該類。這些約束來自JavaScript / TypeScript,與JSweet無關(guān)。
可以想象,使用這種手動(dòng)技術(shù)運(yùn)行簡(jiǎn)單的程序很好,但是對(duì)于開發(fā)復(fù)雜的應(yīng)用程序會(huì)變得非常不舒服。復(fù)雜的應(yīng)用程序大多數(shù)時(shí)候使用適當(dāng)?shù)墓ぞ呃壓?或打包程序,以避免必須手動(dòng)處理JavaScript文件之間的依賴關(guān)系。
為瀏覽器創(chuàng)建捆綁包
為了避免必須手動(dòng)處理依賴項(xiàng),程序員使用捆綁工具將其類捆綁到一個(gè)文件中。這樣的包使用以下內(nèi)容包含在任何網(wǎng)頁(yè)中:
[...]
JSweet帶有這樣的捆綁設(shè)施。要?jiǎng)?chuàng)建一個(gè)包文件,只需設(shè)定true的bundleJSweet的選項(xiàng)。請(qǐng)注意,您還可以設(shè)置true的declaration,詢問JSweet生成打字稿定義文件(可選bundle.d.ts)。此文件允許您以類型合適的方式使用/編譯來自TypeScript的JSweet程序。
JSweet捆綁選項(xiàng)的“神奇之處”在于它分析源代碼中的依賴關(guān)系,并在構(gòu)建捆綁時(shí)負(fù)責(zé)解決前向引用。特別是,JSweet為靜態(tài)字段和初始化器實(shí)現(xiàn)了一個(gè)惰性初始化機(jī)制,以便分解跨類的靜態(tài)前向引用。程序員沒有具體的附加聲明使其工作(與TypeScript相反)。
請(qǐng)注意,它仍然存在一些小的限制(例如,在使用內(nèi)部和匿名類時(shí)),但很少遇到這些限制,并且將在以后的版本中刪除。
另請(qǐng)注意,如果您隨module 選項(xiàng)一起指定選項(xiàng),JSweet將引發(fā)錯(cuò)誤bundle。
包裝模塊
首先,讓我們從解釋模塊開始,重點(diǎn)關(guān)注Java 包(或TypeScript 命名空間)和模塊之間的區(qū)別。如果您對(duì)差異感到滿意,請(qǐng)?zhí)^此部分。
包和模塊是兩個(gè)相似的概念,但適用于不同的上下文。必須將Java包理解為編譯時(shí)命名空間。它們?cè)试S通過名稱路徑對(duì)程序進(jìn)行編譯時(shí)結(jié)構(gòu)化,具有隱式或顯式可見性規(guī)則。軟件包通常對(duì)程序?qū)嶋H捆綁和部署的方式影響不大。
必須將模塊理解為部署/運(yùn)行時(shí)“捆綁”,這可以required由其他模塊實(shí)現(xiàn)。與Java世界中最接近模塊的概念可能是OSGi包。模塊定義導(dǎo)入和導(dǎo)出的元素,以便它們創(chuàng)建強(qiáng)大的運(yùn)行時(shí)結(jié)構(gòu),可用于獨(dú)立部署軟件組件,從而避免名稱沖突。例如,對(duì)于模塊,兩個(gè)不同的庫(kù)可以定義一個(gè)util.List類,并且實(shí)際上在同一個(gè)VM上運(yùn)行和使用,沒有命名問題(只要庫(kù)被捆綁在不同的模塊中)。
如今,許多庫(kù)都是通過模塊打包和訪問的。在瀏覽器中使用模塊的標(biāo)準(zhǔn)方法是AMD,但在Node.js中它是commonjs模塊系統(tǒng)。
JSweet中的模塊
JSweet支持用于打包的AMD,commonjs和UMD模塊系統(tǒng)。JSweet定義了一個(gè)module選項(xiàng)(值:amd,commonjs或umd)。指定此選項(xiàng)時(shí),JSweet會(huì)根據(jù)簡(jiǎn)單規(guī)則自動(dòng)創(chuàng)建默認(rèn)模塊組織:一個(gè)文件=一個(gè)模塊。
例如,當(dāng)將module選項(xiàng)設(shè)置為打包時(shí)commonjs,可以編寫:
node target/js/x/y/z/MyMainClass.js
哪里MyMainClass包含main方法。
模塊系統(tǒng)將自動(dòng)處理參考,并在需要時(shí)需要其他模塊。在引擎蓋下,JSweet分析了Java import語(yǔ)句并將它們轉(zhuǎn)換為require指令。
注意:一旦使用該module選項(xiàng)編譯程序,就可以使用適當(dāng)?shù)墓ぞ撸ㄈ鏐rowserify)將其打包為捆綁包,這樣可以提供與使用bundleJSweet選項(xiàng)類似的輸出。還要注意,同時(shí)指定時(shí)JSweet會(huì)引發(fā)錯(cuò)誤module和bundle,這是排斥的選項(xiàng)。
外部模塊
使用module選項(xiàng)編譯JSweet程序時(shí),必須將所有外部庫(kù)和組件作為外部模塊。只需使用@Module(name) 注釋,JSweet就可以自動(dòng)需要模塊。在JSweet中,導(dǎo)入或使用帶有注釋的類或成員@Module(name)將在運(yùn)行時(shí)自動(dòng)需要相應(yīng)的模塊。請(qǐng)注意,只有使用該module選項(xiàng)生成代碼時(shí)才會(huì)出現(xiàn)這種情況。如果該module選項(xiàng)處于禁用狀態(tài),@Module 則會(huì)忽略注釋。
package def.jquery;
public final class Globals extends def.js.Object {
... @jsweet.lang.Module("jquery") native public static def.jquery.JQuery $(java.lang.String selector); ...
}
上面的代碼顯示了JSweet jQuery API的摘錄。我們可以注意到,該$函數(shù)帶有注釋@Module("jquery")。因此,對(duì)此函數(shù)的任何調(diào)用都將觸發(fā)jquery模塊的要求 。
注意:未來版本中可能會(huì)提供模塊的手動(dòng)要求概念。但是,自動(dòng)需求對(duì)于大多數(shù)程序員來說已經(jīng)足夠,并且隱藏了必須明確要求模塊的復(fù)雜性。無論是否使用模塊,它還具有使用相同代碼的優(yōu)點(diǎn)。
故障排除:當(dāng)糖果沒有正確定義@Module 注釋時(shí),可以在一個(gè)名為的特殊文件的注釋中強(qiáng)制聲明module_defs.java。例如,要強(qiáng)制將BABYLONBabylonjs candy 的 名稱空間導(dǎo)出為 babylonjs模塊,可以編寫以下文件:
package myprogram;
// declare module "babylonjs" {
// export = BABYLON;
// }
請(qǐng)注意,JSweet項(xiàng)目只能定義一個(gè)module_defs.java文件,該文件應(yīng)包含注釋中的所有模塊聲明。另請(qǐng)注意,這是一個(gè)黑客攻擊,首選方法是為糖果做出貢獻(xiàn)來解決問題。
根包
Root包是一種調(diào)整生成的代碼的方法,以便在生成的代碼中擦除JSweet包,從而在運(yùn)行時(shí)刪除。要設(shè)置根包,只需定義一個(gè)package-info.java文件并使用@Root 包上的注釋,如下所示:
@Root
package a.b.c;
上述聲明意味著該c包是一個(gè)根包,即它將在生成的代碼及其所有父包中被刪除。因此,如果c包含一個(gè)包d和一個(gè)類C,它們將是運(yùn)行時(shí)的頂級(jí)對(duì)象。換句話說,a.b.c.d成為 d,a.b.c.C變成C。
請(qǐng)注意,由于在封裝之前放置的@Root封裝被擦除,因此在封裝之前不能定義任何類型@Root。在前面的示例中,a和b包必須是空包。
不使用模塊時(shí)的行為(默認(rèn))
默認(rèn)情況下,root包不會(huì)更改生成的文件的文件夾層次結(jié)構(gòu)。例如,a.b.c.C該類仍將在
不使用模塊時(shí)(默認(rèn)),可以有多個(gè)@Root 包(但@Root包不能包含另一個(gè)@Root 包)。
使用模塊時(shí)的行為
使用模塊時(shí)(請(qǐng)參閱模塊選項(xiàng)),只@Root允許一個(gè)@Root包,當(dāng)有一個(gè)包時(shí),其他包或類型不能超出該@Root包的范圍。然后,生成的文件夾/文件層次結(jié)構(gòu)從根包開始,以便實(shí)際擦除之前的所有文件夾。
包裝JSweet jar(糖果)
糖果是一種Maven工件,包含從JSweet客戶端程序輕松訪問JavaScript庫(kù)所需的所有內(nèi)容。該庫(kù)可以是外部JavaScript庫(kù),TypeScript程序或其他JSweet程序。
糖果的解剖學(xué)
與
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105552.html
摘要:例如允許的對(duì)象默認(rèn)情況下,通過使用內(nèi)置宏將核心對(duì)象和方法映射到。例如這被轉(zhuǎn)換為以下代碼類可以定義構(gòu)造函數(shù),具有超類,并且可以像在中一樣實(shí)例化。因此,它不違反原則。用于聲明該對(duì)象可以用作構(gòu)造函數(shù)。 這個(gè)工具可以將java代碼轉(zhuǎn)為js代碼,從而可以使用java編寫前端代碼 如果排版看著費(fèi)勁可以下載下方html,打開html后使用google翻譯 JSweet語(yǔ)言規(guī)范版本:2.x(快照) 作...
摘要:語(yǔ)言行為及特征狀態(tài)看不懂任何英語(yǔ)技術(shù),英語(yǔ)文檔,凡事沒有培訓(xùn)部在搞的,只有英文文檔的東西國(guó)內(nèi)一律沒大公司在用,都非主流,排斥英文文檔和新技術(shù),以及各種超出他學(xué)習(xí)能力范圍的技術(shù)。 在撰寫此文前首先必須申明的是本人不鄙視任何一種框架,也無意于挑起PHP框架間的戰(zhàn)爭(zhēng),更沒有貶低某個(gè)框架使用者的用意,本文純粹個(gè)人的看法。你可以認(rèn)為我無知也好,或者裝逼也好,請(qǐng)不要試著在任何情況下,隨便發(fā)起言語(yǔ)的...
摘要:每次都是自己手動(dòng)切換下中文顯示。需求分析點(diǎn)擊英文的搜索結(jié)果自動(dòng)跳轉(zhuǎn)到中文文檔地址。其實(shí)我要做的也很簡(jiǎn)單,就是在頁(yè)面加載的時(shí)候,使表單選擇中文簡(jiǎn)體然后重新提交一次表單即可。解決方案解決方案就是使用代碼選擇中文簡(jiǎn)體并且提交表單。 MDN社區(qū)(即Mozilla開發(fā)者社區(qū))具有很多高質(zhì)量中英文文檔。它是我開發(fā)時(shí)遇到概念模糊的地方經(jīng)常訪問的網(wǎng)站。因?yàn)槟J(rèn)搜索一些代碼,優(yōu)先顯示的都是英文。但是恰恰...
摘要:而程序員和醫(yī)生律師的不同點(diǎn)在于持續(xù)學(xué)習(xí)上。兩個(gè)小問題是需要收費(fèi),一年大概刀圖書都是英文的。的視頻基本都有英文字幕,配合作者的,英語(yǔ)不好的同學(xué)學(xué)習(xí)也沒有問題。英文好的有技術(shù)功底的同學(xué)多發(fā)表一些觀點(diǎn),其他的同學(xué)都 摘要: 行業(yè)發(fā)展得太快,你必須學(xué)習(xí),純靠經(jīng)驗(yàn)積累行不通,技術(shù)淘汰的速度遠(yuǎn)大于你經(jīng)驗(yàn)積累的速度。 非雞湯:不要和程序員談自己的編程歷史,很多的經(jīng)驗(yàn)在今天已經(jīng)不適用了。只要2-3年...
摘要:代碼代碼戳這里插件預(yù)備知識(shí)首先給出一本參考的中文書籍,在練習(xí)的過程中有幫到忙。你還可以重寫別的頁(yè)面,比如書簽管理頁(yè)面等,可以參考文檔中文翻譯過來應(yīng)該叫內(nèi)容腳本,它可以運(yùn)行在你指定的頁(yè)面之中,可以拿到指定頁(yè)面的一些信息。 前言 這是一篇關(guān)于Chrome擴(kuò)展插件入門、Vue.js入門的小練習(xí),功能是:在當(dāng)前瀏覽的頁(yè)面點(diǎn)擊擴(kuò)展圖標(biāo),并點(diǎn)擊保存之后,該頁(yè)面就會(huì)存在你的新標(biāo)簽頁(yè)中。其實(shí)就是一個(gè)可...
閱讀 3456·2021-09-08 10:46
閱讀 1185·2019-08-30 13:17
閱讀 2362·2019-08-30 13:05
閱讀 1207·2019-08-29 15:29
閱讀 2886·2019-08-29 11:31
閱讀 538·2019-08-26 12:13
閱讀 1535·2019-08-26 11:42
閱讀 1828·2019-08-23 18:37