Obsah
1. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem
2. Typový systém programovacího jazyka JavaScript
3. Typový systém programovacího jazyky Go
4. Zavolání funkce naprogramované v Go z JavaScriptu
5. První demonstrační příklad: zavolání Go funkce bez předání argumentů
6. Typ argumentů funkce volané z JavaScriptu
7. Druhý demonstrační příklad: výpis všech argumentů předaných funkci volané z JavaScriptu
8. Třetí demonstrační příklad: výpis typu argumentů, které jsou obaleny strukturou js.Value
9. Metoda js.Value.Type() a identifikace typu js.Type
10. Čtvrtý demonstrační příklad: vylepšené řešení tisku argumentů funkce volané z JavaScriptu
11. Konverze předaných argumentů na hodnoty kompatibilní s jazykem Go
13. Pátý demonstrační příklad: realizace předání a konverze dvou argumentů typu celé číslo
14. Šestý demonstrační příklad: realizace předání a konverze dvou argumentů typu double
15. Vrácení hodnot z Go do volajícího programu v JavaScriptu
16. Explicitní převod na JavaScriptovou hodnotu
17. Sedmý demonstrační příklad: vrácení hodnoty typu int do JavaScriptu
18. Osmý demonstrační příklad: zjednodušené vrácení hodnoty typu int do JavaScriptu
19. Repositář s demonstračními příklady
1. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem
Na článek GopherJS: transpřekladač z jazyka Go do JavaScriptu, který vyšel minulý týden, dnes navážeme. Řekneme si totiž, jakým způsobem se vlastně předávají hodnoty (argumenty) mezi kódem napsaným v JavaScriptu na jedné straně a funkcemi a metodami naprogramovanými v jazyce Go na straně druhé. To se sice může na první pohled zdát jako triviální úloha, ovšem kvůli tomu, že se typové systémy obou programovacích jazyků v mnoha ohledech odlišují, vyžaduje předávání hodnot poměrně mnoho podpůrného kódu na straně jazyka Go (minimálně v současné verzi Go).
Nejprve si řekneme, jak se předávají hodnoty jednoduchých (primitivních) datových typů a příště se zaměříme na složené datové typy, tedy zejména pole, řezy, mapy, struktury a obecně „objekty“ (i když tento pojem v Go vlastně, striktně řečeno, neexistuje; ovšem v JavaScriptu je prakticky vše kromě primitivní hodnoty objektem).
2. Typový systém programovacího jazyka JavaScript
Nejprve se, prozatím ve stručnosti, podívejme na typový systém programovacího jazyka JavaScript. Najdeme v něm sedm primitivních datových typů a dále typ Object:
(totéž platí i pro zápornou mezní hodnotu)
# | Datový typ | Stručný popis |
---|---|---|
1 | Boolean | datový typ se dvěma možnými hodnotami true a false |
2 | String | sekvence znaků (jediný znak je taktéž považován za řetězec) |
3 | Number | reprezentuje hodnoty typu double podle IEEE 754; řada celých čísel je tedy omezena mezní hodnotou 253-1 (vlastně šířkou mantisy, i když skutečnost je nepatrně složitější, dtto pro zápornou mezní hodnotu) |
4 | Bigint | speciální typ celočíselných numerických hodnot používaných v případě, že se ukládají hodnoty větší než 253-1 |
5 | Null | typ s jedinou hodnotou null |
6 | Undefined | specialita JavaScriptu (a bolehlav), představuje ještě nepřiřazenou hodnotu (pozor: rozdílné od Null) |
7 | Symbol | unikátní identifikátor |
8 | Object | objekty (je to na první pohled zvláštní, ale sem spadají například i funkce) |
3. Typový systém programovacího jazyky Go
Typový systém programovacího jazyka Go se od JavaScriptu poměrně značně odlišuje, což je ostatně patrné již při pohledu na počet dostupných standardních typů a například na striktní rozdělení celočíselných datových typů od numerických typů s plovoucí řádovou čárkou (floating point). Celou hierarchii typového systému programovacího jazyka Go, která obsahuje všechny v současnosti podporované datové typy i jejich základní vztahy, si můžeme vizualizovat následujícím způsobem:
- Jednoduché datové typy
- Ordinální
- Pravdivostní typ (boolean)
- Celočíselné typy (integer)
- Kód znaku v Unicode (rune) jako speciální případ celočíselného typu
- Neordinální
- Hodnoty s plovoucí řádovou čárkou (float)
- Komplexní čísla (complex)
- Ordinální
- Složené datové typy
- Řetězce (string)
- Pole (array)
- Řezy (slice)
- Mapy (map)
- Záznamy (struct)
- Zvláštní datové typy
- Ukazatel (pointer)
- Funkce (function) – ano, to je taktéž plnohodnotný datový typ
- Rozhraní (interface)
- Kanál (channel)
4. Zavolání funkce naprogramované v Go z JavaScriptu
Již v předchozím článku jsme si ukázali, jakým způsobem je možné z JavaScriptu zavolat funkci naprogramovanou v jazyce Go (s jejím následným transpřekladem do JavaScriptu či do WebAssembly). Takové funkci se předává argument this (lze tedy realizovat volání metody) a dále řez libovolných JavaScriptových hodnot, přesněji řečeno hodnot, které jsou obaleny do typu syscall/js.Value (což si vysvětlíme v navazující textu). Taková funkce navíc může vracet libovolnou hodnotu, protože jméno any je typ odpovídající prázdnému rozhraní (interface{}):
// funkce, která se bude z JavaScriptu tak, jakoby // se jednalo o nativní JavaScriptovou funkci func MojeFunkce(this js.Value, args []js.Value) any { // je nutné vrátit nějakou hodnotu return nil }
Tuto funkci je nutné zaregistrovat tak, aby byla viditelná z JavaScriptového virtuálního stroje. Přitom lze specifikovat jméno funkce z pohledu JavaScriptu (může být odlišné od jména funkce v Go):
js.Global().Set("jmenoFukce", js.FuncOf(PrintHello))
5. První demonstrační příklad: zavolání Go funkce bez předání argumentů
V dnešním prvním demonstračním příkladu je ukázáno, jak lze z JavaScriptu zavolat funkci naprogramovanou v Go a následně transpřeloženou do JavaScriptu nebo do WebAssembly. Této funkci prozatím nepředáváme žádné argumenty a funkce nevrací žádnou „rozumnou“ hodnotu, takže se jedná o ten nejjednodušší možný případ:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintHello(this js.Value, args []js.Value) any { fmt.Println("function PrintHello called") // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintHello tak, aby byla volatelná // z JavaScriptu js.Global().Set("printHello", js.FuncOf(PrintHello)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Tato funkce je do virtuálního stroje JavaScriptu zaregistrována pod jménem „printHello“ a pod tímto jménem je i zavolána:
<!doctype html> <html> <head> <title>Function call: without arguments</title> </head> <body> <h1 id="header">Function call: without arguments</h2> <script src="func_call_no_arguments.js" type="text/javascript"></script> <script type="text/javascript"> printHello(); </script> </body> </html>
Výsledek je zobrazen v konzoli webového prohlížeče:

Obrázek 1: Funkce naprogramovaná v Go je skutečně zavolána z JavaScriptu.
6. Typ argumentů funkce volané z JavaScriptu
Podívejme se ještě jednou na hlavičku funkce PrintHello(), kterou je možné (po registraci) volat i z JavaScriptu:
func PrintHello(this js.Value, args []js.Value) any { // je nutné vrátit nějakou hodnotu return nil }
Této funkci se předává jeden parametr this a dále řez (tedy dopředu neznámý počet prvků). V obou případech je ovšem použit totožný typ js.Value deklarovaný v balíčku syscall/js. S vlastnostmi tohoto typu se podrobněji seznámíme v navazujícím textu, už nyní je ale vhodné si vypsat všechny jeho viditelné metody (interní strukturu nemáme k dispozici):
func (v Value) Bool() bool func (v Value) Call(m string, args ...any) Value func (v Value) Delete(p string) func (v Value) Equal(w Value) bool func (v Value) Float() float64 func (v Value) Get(p string) Value func (v Value) Index(i int) Value func (v Value) InstanceOf(t Value) bool func (v Value) Int() int func (v Value) Invoke(args ...any) Value func (v Value) IsNaN() bool func (v Value) IsNull() bool func (v Value) IsUndefined() bool func (v Value) Length() int func (v Value) New(args ...any) Value func (v Value) Set(p string, x any) func (v Value) SetIndex(i int, x any) func (v Value) String() string func (v Value) Truthy() bool func (v Value) Type() Type
V balíčku syscall/js je deklarována je i čtveřice funkcí, které dovolují hodnotu typu js.Value vytvořit či nějakým způsobem získat:
func Global() Value func Null() Value func Undefined() Value func ValueOf(x any) Value
7. Druhý demonstrační příklad: výpis všech argumentů předaných funkci volané z JavaScriptu
Ukažme si nyní, jakým postupem se zobrazí počet a hodnoty všech argumentů, které jsou předány funkci naprogramované v jazyce Go, jež je volána z JavaScriptu. Je to ve skutečnosti snadné. Počet argumentů se zjistí standardní funkcí len(), protože všechny argumenty jsou předány v jediném řezu. A hodnoty argumentů můžeme zobrazit funkcí fmt.Printf v případě, že použijeme formátovací znak %v. Ten způsobí, že se hodnoty (interně libovolného typu) převedenou na řetězec a ten se následně zobrazí.
V Go tedy můžeme napsat funkci PrintArguments, která provede výše uvedené operace:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintArguments(this js.Value, args []js.Value) any { fmt.Println("function PrintArguments called") fmt.Printf("# of parameters: %d\n", len(args)) for i, arg := range args { fmt.Printf("parameter # %d: %v\n", i, arg) } // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintArguments tak, aby byla volatelná // z JavaScriptu js.Global().Set("printArguments", js.FuncOf(PrintArguments)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Tuto funkci několikrát zavoláme z JavaScriptového kódu napsaného přímo v HTML stránce. Povšimněte si, že předáváme různý počet argumentů s různými typy:
<!doctype html> <html> <head> <title>Function call: with arguments</title> </head> <body> <h1 id="header">Function call: with arguments</h2> <script src="func_call_with_arguments.js" type="text/javascript"></script> <script type="text/javascript"> printArguments(); console.log(); printArguments(1); console.log(); printArguments(1, 2); console.log(); printArguments("foo", "bar", "baz"); console.log(); printArguments(true, false); console.log(); printArguments(null); console.log(); printArguments(undefined); console.log(); </script> </body> </html>
Výsledek bude opět viditelný v konzoli webového prohlížeče:

Obrázek 2: Naše testovací funkce byla několikrát zavolána s různým počtem i hodnotami argumentů.
8. Třetí demonstrační příklad: výpis typu argumentů, které jsou obaleny strukturou js.Value
V dnešním třetím demonstračním příkladu je ukázán ten nejjednodušší dostupný způsob zjištění konkrétního typu argumentu, který je předán z JavaScriptu do funkce naprogramované v jazyce Go. Už z předchozího příkladu je totiž patrné, že hodnoty typu js.Value vlastně pouze vhodným způsobem „obalují“ konkrétní hodnoty. Pokusme se tedy typy argumentů vypsat tak, že použijeme funkci fmt.Printf společně s formátovacím znakem %T
for i, arg := range args { fmt.Printf("parameter # %d with type %T: %v\n", i, arg, arg) }
Celý zdrojový kód napsaný v jazyce Go se tedy do značné míry podobá předchozímu příkladu, liší se jen způsobem výpisu argumentů:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - metoda PrintArguments vytiskne typy svých argumentů package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintArguments(this js.Value, args []js.Value) any { fmt.Println("function PrintArguments called") fmt.Printf("# of parameters: %d\n", len(args)) for i, arg := range args { fmt.Printf("parameter # %d with type %T: %v\n", i, arg, arg) } // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintArguments tak, aby byla volatelná // z JavaScriptu js.Global().Set("printArguments", js.FuncOf(PrintArguments)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Z HTML stránky opět funkci PrintArguments (resp. z pohledu JavaScriptu spíše printArguments) budeme volat s různými počty a typy argumentů:
<!doctype html> <html> <head> <title>Function call: with arguments</title> </head> <body> <h1 id="header">Function call: with arguments</h2> <script src="func_call_argument_types_1.js" type="text/javascript"></script> <script type="text/javascript"> printArguments(); console.log(); printArguments(1); console.log(); printArguments(1, 2); console.log(); printArguments("foo", "bar", "baz"); console.log(); printArguments(true, false); console.log(); printArguments(null); console.log(); printArguments(undefined); console.log(); </script> </body> </html>
Podívejme se na výsledky vypsané tímto demonstračním příkladem do webové konzole:

Obrázek 3: Hodnoty i typy parametrů vypsané třetím demonstračním příkladem do konzole webového prohlížeče.
9. Metoda js.Value.Type() a identifikace typu js.Type
V mnoha situacích je nutné konkrétní hodnotu předaných argumentů nejenom vytisknout na standardní výstup, ale získat v podobě vhodné pro jejich další zpracování (například pro provedení konverze atd.). Přesně k tomuto účelu slouží metoda Type datového typu js.Value. Pro každý předaný argument (jak this, tak i pro každý prvek předaného řezu) tedy můžeme zavolat tuto metodu:
func (v Value) Type() Type
Tato metoda vrací hodnotu typu js.Type, což ovšem není nic jiného, než typ odvozený od celého čísla:
type Type int
Konkrétně se může vrátit jedna z následujících konstant:
const ( TypeUndefined Type = iota TypeNull TypeBoolean TypeNumber TypeString TypeSymbol TypeObject TypeFunction )
V aplikacích tedy můžeme velmi zjistit, jakého typu je libovolný z předaných argumentů. V současnosti je to vlastně jediná rozumná možnost, jak zahájit konverzi argumentů na hodnoty kompatibilní s jazykem Go, jak ostatně uvidíme níže.
10. Čtvrtý demonstrační příklad: vylepšené řešení tisku argumentů funkce volané z JavaScriptu
V dnešním čtvrtém demonstračním příkladu je ukázáno, jakým způsobem je možné realizovat vylepšené řešení získání a tisku typů argumentů funkce napsané v jazyku Go, která je volána z JavaScriptu. Pro získání typu argumentu použijeme metodu js.Value.Type(), která vrátí hodnotu, kterou můžeme snadno převést na řetězec zavoláním metody js.Type.String(). Tisk pozic, hodnot a typů předávaných argumentů tedy můžeme realizovat touto programovou smyčkou:
for i, arg := range args { fmt.Printf("parameter # %d with type '%s': %s\n", i, arg.Type().String(), arg.String()) }
Úplný zdrojový kód tohoto příkladu vypadá následovně:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - metoda PrintArguments vytiskne typy svých argumentů // - lepší řešení založené na typu js.Type package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintArguments(this js.Value, args []js.Value) any { fmt.Println("function PrintArguments called") fmt.Printf("# of parameters: %d\n", len(args)) for i, arg := range args { fmt.Printf("parameter # %d with type '%s': %s\n", i, arg.Type().String(), arg.String()) } // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintArguments tak, aby byla volatelná // z JavaScriptu js.Global().Set("printArguments", js.FuncOf(PrintArguments)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Příslušná HTML stránka, z níž se volá zaregistrovaná funkce printArguments:
<!doctype html> <html> <head> <title>Function call: with arguments</title> </head> <body> <h1 id="header">Function call: with arguments</h2> <script src="func_call_argument_types_2.js" type="text/javascript"></script> <script type="text/javascript"> printArguments(); console.log(); printArguments(1); console.log(); printArguments(1, 2); console.log(); printArguments("foo", "bar", "baz"); console.log(); printArguments(true, false); console.log(); printArguments(null); console.log(); printArguments(undefined); console.log(); </script> </body> </html>
Opět se podívejme na výsledky vypsané tímto demonstračním příkladem do webové konzole:

Obrázek 4: Hodnoty i typy parametrů vypsané čtvrtým demonstračním příkladem do konzole webového prohlížeče.
11. Konverze předaných argumentů na hodnoty kompatibilní s jazykem Go
Z předchozí dvojice demonstračních příkladů je patrné, že hodnoty předávané do funkce naprogramované v jazyce Go (přes řez), nemají typy kompatibilní s jazykem Go. Abychom získali hodnoty základních datových typů, je nutné provést explicitní datové konverze. A ještě před provedením konverze je nutné zjistit, hodnota jakého typu je vlastně předávána. Není totiž možné provést konverzi z například JavaScriptového řetězce na celé číslo. Kontrola, jakého typu je předávaná hodnota, je relativně snadná, i když se (v současnosti) musí provádět ručně. Například pro i-tý předávaný argument můžeme zjistit, jestli se z pohledu JavaScriptu jedná o číslo (Number) či nikoli:
// přečíst typ i-tého argumentu typ := args[i].Type() // zkontrolovat, zda se předává numerická hodnota if typ != js.TypeNumber { fmt.Printf("Argument #%d has incorrect type %s\n", index, typ.String()) return nil }
12. Dostupné konverzní metody
Předávané argumenty jsou, jak již dobře víme, typu js.Value. Pro tento typ jsou definovány čtyři konverzní metody, které vrací přímo hodnotu nativního Go typu. Tyto metody postupně vrací pravdivostní hodnotu, celé číslo, číslo s plovoucí řádovou čárkou nebo řetězec. Pro ty typy JavaScriptu, které nemají přímou obdobu v Go, pak existují metody vracející pravdivostní hodnotu – zda se předává null, undefined nebo Not a Number (což je ale vlastně současně float64):
Metoda | Návratový typ |
---|---|
func (v Value) Bool() | bool |
func (v Value) Int() | int |
func (v Value) Float() | float64 |
func (v Value) String() | string |
func (v Value) IsNaN() | bool |
func (v Value) IsNull() | bool |
func (v Value) IsUndefined() | bool |
13. Pátý demonstrační příklad: realizace předání a konverze dvou argumentů typu celé číslo
V dnešním pátém demonstračním příkladu je ukázána realizace funkce PrintSum naprogramované v jazyce Go, která vyžaduje, aby byla z JavaScriptu zavolána s předáním dvojice numerických hodnot, které jsou následně zkontrolovány a poté zkonvertovány na celé číslo. Pokud jsou předány odlišné parametry (či pokud je jich předáno více či naopak méně), funkce vypíše chybové hlášení a je ukončena:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintSum // - kontrola typu argumentů předaných funkci PrintSum // - provedení konverze na nativní typy jazyka Go package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintSum(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 2 { fmt.Printf("incorrect number of arguments %d, but just two are accepted\n", len(args)) return nil } // kontrola typu předaných argumentů // (pozor - původní syntaxe zápisu smyčky) for index := 0; index < 2; index++ { typ := args[index].Type() if typ != js.TypeNumber { fmt.Printf("Argument #%d has incorrect type %s\n", index, typ.String()) return nil } } // počet i typ argumentů je korektní // lze tedy provést jejich konverzi x := args[0].Int() y := args[1].Int() // vypočítat výsledek z := x + y // zobrazit výsledek fmt.Printf("%d + %d = %d\n", x, y, z) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintSum tak, aby byla volatelná // z JavaScriptu js.Global().Set("printSum", js.FuncOf(PrintSum)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Příslušná HTML stránka, z níž funkci PrintSum voláme s různými počty a typy argumentů, vypadá následovně:
<!doctype html> <html> <head> <title>Function call: with two integer arguments</title> </head> <body> <h1 id="header">Function call: with two integer arguments</h2> <script src="func_call_two_ints.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty printSum(1, 2); printSum(10, 20); printSum(100, 200); printSum(-1, -2); // špatný počet argumentů printSum(10, 10, 20); printSum(10); // špatný typ argumentů printSum("1", 2); printSum(1, "2"); printSum(1, true); printSum(1, null); // mezní případ printSum(1.1, 2.2); </script> </body> </html>

Obrázek 5: Funkce PrintSum vypisuje své výsledky i chybová hlášení do webové konzole.
14. Šestý demonstrační příklad: realizace předání a konverze dvou argumentů typu double
Šestý demonstrační příklad se do značné míry podobá příkladu předchozímu, ovšem namísto konverze předané dvojice argumentů na typ int se pokoušíme o konverzi na typ double, což by mělo být prakticky ve všech případech možné:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintSum // - kontrola typu argumentů předaných funkci PrintSum // - provedení konverze na nativní typy jazyka Go package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintSum(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 2 { fmt.Printf("incorrect number of arguments %d, but just two are accepted\n", len(args)) return nil } // kontrola typu předaných argumentů // (pozor - původní syntaxe zápisu smyčky) for index := 0; index < 2; index++ { typ := args[index].Type() if typ != js.TypeNumber { fmt.Printf("Argument #%d has incorrect type %s\n", index, typ.String()) return nil } } // počet i typ argumentů je korektní // lze tedy provést jejich konverzi x := args[0].Float() y := args[1].Float() // vypočítat výsledek z := x + y // zobrazit výsledek fmt.Printf("%g + %g = %g\n", x, y, z) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintSum tak, aby byla volatelná // z JavaScriptu js.Global().Set("printSum", js.FuncOf(PrintSum)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Příslušná HTML stránka je opět prakticky totožná s příkladem předchozím, tedy:
<!doctype html> <html> <head> <title>Function call: with two FP arguments</title> </head> <body> <h1 id="header">Function call: with two FP arguments</h2> <script src="func_call_two_floats.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty printSum(1.1, 2.2); printSum(1e10, 1e10); printSum(1e100, 1e100); // špatný počet argumentů printSum(10, 10, 20); printSum(10); // špatný typ argumentů printSum("1", 2.0); printSum(1.0, "2"); printSum(1.0, true); printSum(1.0, null); </script> </body> </html>

Obrázek 6: Funkce PrintSum vypisuje své výsledky i chybová hlášení do webové konzole. Tentokrát jsou výpočty provedeny s hodnotami typu double.
15. Vrácení hodnot z Go do volajícího programu v JavaScriptu
V závěrečné části dnešního článku si vysvětlíme, jakým způsobem se vrací hodnoty z Go do JavaScriptu. Prozatím se omezíme na primitivní datové typy. Připomeňme si nejdříve, jak vypadá hlavička funkce naprogramované v jazyce Go, ale volatelné z JavaScriptu (po registraci takové funkce):
// funkce, která se bude z JavaScriptu tak, jakoby // se jednalo o nativní JavaScriptovou funkci func MojeFunkce(this js.Value, args []js.Value) any { // je nutné vrátit nějakou hodnotu return nil }
Povšimněte si, že návratový typ je any, což odpovídá prázdnému rozhraní. Můžeme tedy vracet „cokoli“, ovšem samozřejmě i zde existují pravidla pro typovou konverzi mezi Go a JavaScriptem.
16. Explicitní převod na JavaScriptovou hodnotu
Funkce naprogramovaná v jazyce Go může do JavaScriptu vrátit výsledek své činnosti. Přitom se vrací jen jediná hodnota – nemůžeme zde tedy využít možnost nabízenou jazykem Go a vrátit větší množství hodnot. Typ návratové hodnoty funkce je any, což může být zpočátku matoucí. Ovšem pro převod hodnoty nějakého Go typu na JavaScriptovou hodnotu můžeme použít funkci z balíčku syscall/js, která se jmenuje ValueOf. Tato funkce převede (prakticky jakoukoli) hodnotu z Go na její JavaScriptový protějšek typu Value:
func ValueOf(x any) Value
Příklad použití pro proměnnou x, která může být teoreticky libovolného typu:
// funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func Funkce(this js.Value, args []js.Value) any { ... ... ... // vrátit výsledek s explicitní konverzí return js.ValueOf(x) }
17. Sedmý demonstrační příklad: vrácení hodnoty typu int do JavaScriptu
Podívejme se, jak by mohla vypadat realizace funkce pro součet hodnot dvou předaných argumentů. Nejedná se o krátký kód, protože musíme zkontrolovat počty a typy předávaných argumentů a následně provést jejich konverzi na primitivní typy jazyka Go (což již známe). A výsledek součtu konvertujeme zpět na JavaScriptovou hodnotu funkcí js.ValueOf. Tato hodnota se následně z Go funkce vrací a je zpracována virtuálním strojem JavaScriptu:
// Technologie WebAssembly // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci CalcSum // - kontrola typu argumentů předaných funkci CalcSum // - provedení konverze na nativní typy jazyka Go // - převedení výsledku zpět na JavaScriptový typ package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func CalcSum(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 2 { fmt.Printf("incorrect number of arguments %d, but just two are accepted\n", len(args)) return nil } // kontrola typu předaných argumentů // (pozor - původní syntaxe zápisu smyčky) for index := 0; index < 2; index++ { typ := args[index].Type() if typ != js.TypeNumber { fmt.Printf("Argument #%d has incorrect type %s\n", index, typ.String()) return nil } } // počet i typ argumentů je korektní // lze tedy provést jejich konverzi x := args[0].Int() y := args[1].Int() // vypočítat výsledek z := x + y // vrátit výsledek s explicitní konverzí return js.ValueOf(z) } func main() { fmt.Println("started") c := make(chan bool) // export funkce CalcSum tak, aby byla volatelná // z JavaScriptu js.Global().Set("calcSum", js.FuncOf(CalcSum)) // realizace nekonečného čekání <-c fmt.Println("finished") }
Do příslušné HTML stránky je vloženo několik volání funkce calcSum se zobrazením jejího výsledku (návratové hodnoty) do webové konzole:
<!doctype html> <html> <head> <title>Function call: returning integer value</title> </head> <body> <h1 id="header">Function call: returning integer value</h2> <script src="func_call_return_int_1.js" type="text/javascript"></script> <script type="text/javascript"> <i>// korektní argumenty</i> console.log(calcSum(1, 2)); console.log(calcSum(10, 20)); console.log(calcSum(100, 200)); console.log(calcSum(-1, -2)); </script> </body> </html>
A takto by měly vypadat vypočtené výsledky:

Obrázek 7: Výsledky součtu různých hodnot zobrazené ve webové konzoli.
18. Osmý demonstrační příklad: zjednodušené vrácení hodnoty typu int do JavaScriptu
Ve skutečnosti je rozhraní mezi Go a jazykem JavaScript realizováno takovým způsobem, že z funkce naprogramované v Go je možné vracet libovolnou hodnotu bez jejího explicitního převodu pomocí konverzní funkce js.ValueOf. Ukázkový příklad z předchozí kapitoly lze tedy nepatrně zjednodušit do podoby, kdy z Go funkce přímo vracíme hodnotu proměnné typu int:
// Technologie WebAssembly // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci CalcSum // - kontrola typu argumentů předaných funkci CalcSum // - provedení konverze na nativní typy jazyka Go // - automatická konverze výsledku zpět na JavaScriptový typ package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func CalcSum(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 2 { fmt.Printf("incorrect number of arguments %d, but just two are accepted\n", len(args)) return nil } // kontrola typu předaných argumentů // (pozor - původní syntaxe zápisu smyčky) for index := 0; index < 2; index++ { typ := args[index].Type() if typ != js.TypeNumber { fmt.Printf("Argument #%d has incorrect type %s\n", index, typ.String()) return nil } } // počet i typ argumentů je korektní // lze tedy provést jejich konverzi x := args[0].Int() y := args[1].Int() // vypočítat výsledek z := x + y // vrátit výsledek s automatickou konverzí return z } func main() { fmt.Println("started") c := make(chan bool) // export funkce CalcSum tak, aby byla volatelná // z JavaScriptu js.Global().Set("calcSum", js.FuncOf(CalcSum)) // realizace nekonečného čekání <-c fmt.Println("finished") }
HTML stránka zůstane totožná, jako tomu bylo v příkladu předchozím, pouze pochopitelně importujeme jiný (transpilovaný) zdrojový kód:
<!doctype html> <html> <head> <title>Function call: returning integer value</title> </head> <body> <h1 id="header">Function call: returning integer value</h2> <script src="func_call_return_int_2.js" type="text/javascript"></script> <script type="text/javascript"> <i>// korektní argumenty</i> console.log(calcSum(1, 2)); console.log(calcSum(10, 20)); console.log(calcSum(100, 200)); console.log(calcSum(-1, -2)); </script> </body> </html>

Obrázek 8: Výsledky součtu různých hodnot zobrazené ve webové konzoli.
19. Repositář s demonstračními příklady
Demonstrační příklady napsané v jazyce Go, které jsou určené pro transpřeklad do JavaScriptu s využitím nástroje GopherJS, byly uloženy do Git repositáře, jenž je dostupný na adrese https://github.com/RedHatOfficial/GoCourse. Jednotlivé demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:
# | Příklad | Stručný popis | Adresa |
---|---|---|---|
1 | func_call_no_arguments.go | první demonstrační příklad: zavolání Go funkce bez předání argumentů | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_no_arguments.go |
2 | func_call_no_arguments.html | HTML stránka s kódem pro načtení prvního demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_no_arguments.html |
3 | func_call_with_arguments.go | druhý demonstrační příklad: výpis všech argumentů předaných funkci volané z JavaScriptu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_with_arguments.go |
4 | func_call_with_arguments.html | HTML stránka s kódem pro načtení druhého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_with_arguments.html |
5 | func_call_argument_types1.go | třetí demonstrační příklad: výpis typu argumentů, které jsou obaleny strukturou js.Value | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_argument_types1.go |
6 | func_call_argument_types1.html | HTML stránka s kódem pro načtení třetího demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_argument_types1.html |
7 | func_call_argument_types2.go | čtvrtý demonstrační příklad: vylepšené řešení tisku argumentů funkce volané z JavaScriptu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_argument_types2.go |
8 | func_call_argument_types2.html | HTML stránka s kódem pro načtení čtvrtého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_argument_types2.html |
9 | func_call_two_ints.go | pátý demonstrační příklad: realizace předání a konverze dvou argumentů typu celé číslo | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_two_ints.go |
10 | func_call_two_ints.html | HTML stránka s kódem pro načtení pátého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_two_ints.html |
11 | func_call_two_floats.go | šestý demonstrační příklad: realizace předání a konverze dvou argumentů typu double | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_two_floats.go |
12 | func_call_two_floats.html | HTML stránka s kódem pro načtení šestého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_two_floats.html |
13 | func_call_return_int1.go | sedmý demonstrační příklad: vrácení hodnoty typu int do JavaScriptu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_return_int1.go |
14 | func_call_return_int1.html | HTML stránka s kódem pro načtení sedmého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_return_int1.html |
15 | func_call_return_int2.go | osmý demonstrační příklad: vrácení hodnoty typu int do JavaScriptu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_return_int2.go |
16 | func_call_return_int2.html | HTML stránka s kódem pro načtení osmého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_return_int2.html |
Pro úplnost si uveďme i odkazy na ukázkové příklady použité minule:
# | Příklad | Stručný popis | Adresa |
---|---|---|---|
1 | hello_world.go | zdrojový kód prvního demonstračního příkladu: výpis zprávy na konzoli webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/hello_world.go |
1 | hello_world.html | HTML stránka s kódem pro načtení prvního demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/hello_world.html |
2 | dom_manipulation.go | zdrojový kód druhého demonstračního příkladu: manipulace s DOMem webové stránky | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/dom_manipulation.go |
2 | dom_manipulation.html | HTML stránka s kódem pro načtení druhého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/dom_manipulation.html |
3 | dom_add_element.go | zdrojový kód třetího demonstračního příkladu: přidání elementů do DOMu webové stránky | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/dom_add_element.go |
3 | dom_add_element.html | HTML stránka s kódem pro načtení třetího demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/dom_add_element.html |
4 | draw_into_canvas.go | zdrojový kód čtvrtého demonstračního příkladu: kreslení do canvasu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/draw_into_canvas.go |
4 | draw_into_canvas.html | HTML stránka s kódem pro načtení čtvrtého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/draw_into_canvas.html |
5 | js_interop1.go | zdrojový kód pátého demonstračního příkladu: komunikace s JavaScriptem | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/js_interop1.go |
5 | js_interop1.html | HTML stránka s kódem pro načtení pátého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/js_interop1.html |
6 | js_interop2.go | zdrojový kód šestého demonstračního příkladu: komunikace s JavaScriptem | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/js_interop2.go |
6 | js_interop2.html | HTML stránka s kódem pro načtení šestého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/js_interop2.html |
7 | http_server.go | implementace HTTP serveru, který dokáže webovému prohlížeči předávat obsah požadovaných souborů | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/http_server.go |
8 | hello_world2.go | varianta programu typu „Hello, world!“, která volá pouze funkci println() | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/hello_world2.go |
20. Odkazy na Internetu
- go2js
https://github.com/tredoe/go2js - GitHub repositář projektu GopherJS
https://github.com/gopherjs/gopherjs - How to use GopherJS to turn Go code into a JavaScript library
https://medium.com/@kentquirk/how-to-use-gopherjs-to-turn-go-code-into-a-javascript-library-1e947703db7a - Source to source compiler
https://en.wikipedia.org/wiki/Source-to-source_compiler - Binary recompiler
https://en.wikipedia.org/wiki/Binary_recompiler - py2many na GitHubu
https://github.com/py2many/py2many - py2many na PyPi
https://pypi.org/project/py2many/ - Awesome Transpilers
https://github.com/milahu/awesome-transpilers - WebAssembly
https://webassembly.org/ - WebAssembly na Wiki Golangu
https://github.com/golang/go/wiki/WebAssembly - The future of WebAssembly – A look at upcoming features and proposals
https://blog.scottlogic.com/2018/07/20/wasm-future.html - Writing WebAssembly By Hand
https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html - WebAssembly Specification
https://webassembly.github.io/spec/core/index.html - Index of Instructions
https://webassembly.github.io/spec/core/appendix/index-instructions.html - The WebAssembly Binary Toolkit
https://github.com/WebAssembly/wabt - Will WebAssembly replace JavaScript? Or Will WASM Make JavaScript More Valuable in Future?
https://dev.to/vaibhavshah/will-webassembly-replace-javascript-or-will-wasm-make-javascript-more-valuable-in-future-5c6e - Roadmap (pro WebAssemly)
https://webassembly.org/roadmap/ - Transcrypt
https://transcrypt.org/ - JavaScript Data Types
https://www.geeksforgeeks.org/javascript-data-types/ - Standardní balíček syscall/js
https://pkg.go.dev/syscall/js - Data types
https://javascript.info/types - Datové typy (napsáno poněkud zjednodušeně)
https://naucme.it/chapter/qa-04 - Primitive (JavaScript)
https://developer.mozilla.org/en-US/docs/Glossary/Primitive - JavaScript type: String
https://developer.mozilla.org/en-US/docs/Glossary/String - JavaScript type: Number
https://developer.mozilla.org/en-US/docs/Glossary/Number - JavaScript type: Boolean
https://developer.mozilla.org/en-US/docs/Glossary/Boolean - JavaScript type: Undefined
https://developer.mozilla.org/en-US/docs/Glossary/Undefined - JavaScript type: Null
https://developer.mozilla.org/en-US/docs/Glossary/Null - JavaScript type: Symbol
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol - JavaScript type: BigInt
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt