Obsah
1. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem (dokončení)
2. Předání řetězce z JavaScriptu do Go
3. Otestování předání řetězce z JavaScriptu do Go
4. Předání objektu z JavaScriptu do Go
5. Demonstrační příklad: předání JavaScriptového objektu do funkce naprogramované v Go
6. Přístup k atributům JavaScriptového objektu z Go
7. Demonstrační příklad: přečtení vybraných atributů předaného JavaScriptového objektu
8. Konverze atributů předaného objektu do nativních typů Go
9. Demonstrační příklad: konverze všech atributů objektů do nativních typů jazyka Go
10. Předání pole, získání počtu prvků pole
11. Demonstrační příklad: předání pole do Go funkce se zjištěním jeho délky
12. Přístup k prvkům JavaScriptového pole z Go
13. Demonstrační příklad: výpis typů a hodnot všech prvků JavaScriptového pole
14. Součet prvků předaného pole
15. Demonstrační příklad: součet všech prvků pole předaného z JavaScriptu do Go
16. Předání funkce naprogramované v JavaScriptu do Go
17. Zavolání JavaScriptové funkce
18. Demonstrační příklad: zavolání JavaScriptové funkce z jazyka Go
19. Repositář s demonstračními příklady
1. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem (dokončení)
Připomeňme si, že v jazyce JavaScript můžeme pracovat s hodnotami různých typů. Ukázali jsme si i tabulku se základními datovými typy JavaScriptu:
(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) |
Ve skutečnosti ovšem nesmíme zapomenout ještě na jeden datový typ, se kterým se později setkáme. Tímto datovým typem je funkce (function), která je v JavaScriptu (ostatně podobně jako i v některých dalších programovacích jazycích) považována za plnohodnotný datový typ.
Minule jsme si na několika příkladech ukázali, jakým způsobem lze do funkce naprogramované v jazyku Go předat argumenty typu Number. Ty bylo možné zkonvertovat na typ int a float64, s nimiž je možné v Go běžně pracovat (až na problém týkající se potenciálního menšího rozsahu typu int na 32bitových systémech). Naprosto stejným způsobem lze pracovat s pravdivostními hodnotami, které se zkonvertují na typ boolean.
U hodnot typu Null a Undefined se neprovádí skutečná konverze, ale pouze test, zda je zvolený argument typu Null či Undefined (samotný typ přímo určuje i hodnotu). Tyto testy se provádí metodami Value.IsNull() a Value.IsUndefined.
Zbývají nám tedy řetězce, objekty a v neposlední řadě taktéž funkce. A právě těmito typy a jejich konverzemi se budeme zabývat v dnešním článku.
2. Předání řetězce z JavaScriptu do Go
S řetězci se jak v JavaScriptu, tak i v jazyce Go pracuje obdobným způsobem, i když interně se může způsob ukládání řetězců lišit. V obou případech se však jedná o sekvenci Unicode znaků se známou délkou; navíc je tato sekvence v obou jazycích neměnitelná (immutable).
Pro převod JavaScriptového argumentu typu řetězec na plnohodnotný řetězec (z pohledu jazyka Go) se používá metoda Value.String(). Na rozdíl od ostatních metod pro konverzi hodnoty, s nimiž jsme se až doposud setkali (Value.Bool(), Value.Int() a Value.Float()) však metoda Value.String() „nezpanikaří“ ani v případě, kdy je předávaný argument odlišného typu. V takovém případě se provede konverze (libovolného) typu na řetězec „T:V“, kde za T se dosadí jméno typu a za V je dosazena hodnota v řetězcové podobě (a u typů Null a Undefined se uvádí pouze typ, protože ten přímo definuje i hodnotu).
3. Otestování předání řetězce z JavaScriptu do Go
V dnešním prvním demonstračním příkladu je ukázán způsob předání řetězce z JavaScriptu (tj. z libovolného kódu naprogramovaného v JavaScriptu) do funkce naprogramované v jazyku Go. V této funkci zkontrolujeme počet předaných argumentů (to již dobře známe, takže se již nebudu opakovat) a taktéž to, zda je typ prvního (a jediného) argumentu skutečně řetězcem:
// kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeString { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil }
V případě, že je skutečně předán řetězec, provedeme konverzi z JavaScriptového řetězce na Go řetězec:
// provést konverzi message := args[0].String()
Dále je již možné s proměnnou message pracovat stejně, jako s jakýmkoli jiným řetězcem v Go.
Úplný zdrojový kód takto vytvořeného demonstračního příkladu vypadá následovně:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintMessage // - kontrola typu argumentů předaných funkci PrintMessage // - 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 PrintMessage(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } // kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeString { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // provést konverzi message := args[0].String() // zobrazit zprávu fmt.Println(message) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintMessage tak, aby byla volatelná // z JavaScriptu js.Global().Set("printMessage", js.FuncOf(PrintMessage)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
HTML stránka, ve které je funkce printMessage volána s různým typem i počty argumentů, vypadá takto:
<!doctype html> <html> <head> <title>Function call: with string argument</title> </head> <body> <h1 id="header">Function call: with string argument</h2> <script src="func_call_string.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty printMessage(""); printMessage("Hello, world!"); printMessage("příliš žluťoučký kůň"); // špatný počet argumentů printMessage("foo", "bar") printMessage(); // špatný typ argumentu printMessage(2); printMessage(null); printMessage(undefined); </script> </body> </html>
A pochopitelně se podíváme i na výsledek volání funkce printMessage zobrazený v konzoli prohlížeče:

Obrázek 1: Výsledek volání funkce printMessage z JavaScriptu s různými typy a počty předávaných argumentů.
4. Předání objektu z JavaScriptu do Go
Objekty jsou v JavaScriptu používány pro mnoho účelů; navíc do této kategorie spadají například i pole, slovníky atd. Přímo v JavaScriptu lze objekty vytvářet (konstruovat) různými způsoby, například jako instance třídy, funkcionálním stylem atd. Ukažme si tu pravděpodobně nejjednodušší možnost konstrukce objektu, která vypadá následovně:
// konstrukce objektu const person = new Object(); // přidání atributů person.firstName = "John"; person.lastName = "Doe"; person.age = 42; person.eyeColor = "blue";
Takový objekt lze předat do jazyka Go v argumentu volané funkce. V Go bude představován hodnotou typu js.Value. Bude možné zjistit, zda se skutečně jedná o hodnotu typu objekt a navíc lze i přistoupit k atributům tohoto objektu s využitím metody Value.Get (a jak uvidíme dále, existují i další podporované operace).
5. Demonstrační příklad: předání JavaScriptového objektu do funkce naprogramované v Go
Opět si prakticky ukažme, jakým způsobem je možné předat JavaScriptový objekt do funkce, která je naprogramovaná v jazyce Go. V této funkci opět nejprve zkontrolujeme počet předaných argumentů (postup již dobře známe) a následně pak typ předaného argumentu:
object := args[0] // kontrola typu předaného argumentu typ := object.Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil }
V případě, že je předán korektní hodnota typu „objekt“, je textová reprezentace takového objektu vypsána na webovou konzoli.
Implementace části napsané v Go (včetně registrace funkce) může vypadat takto:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintObject // - kontrola typu argumentů předaných funkci PrintObject package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintObject(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } object := args[0] // kontrola typu předaného argumentu typ := object.Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // zobrazit objekt fmt.Println(object) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintObject tak, aby byla volatelná // z JavaScriptu js.Global().Set("printObject", js.FuncOf(PrintObject)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
Ve zdrojovém kódu webové stránky nejdříve vytvoříme nový objekt i s atributy. Tento objekt následně předáme do funkce naprogramované v Go:
<!doctype html> <html> <head> <title>Function call: with object argument</title> </head> <body> <h1 id="header">Function call: with object argument</h2> <script src="func_call_object_1.js" type="text/javascript"></script> <script type="text/javascript"> // konstrukce objektu const person = new Object(); // přidání atributů person.firstName = "John"; person.lastName = "Doe"; person.age = 42; person.eyeColor = "blue"; // zavolání funkce naprogramované v Go s předáním objektu printObject(person); </script> </body> </html>
Výsledek zobrazený na webové konzoli by měl vypadat následovně:

Obrázek 2: Výsledek volání funkce printObject z JavaScriptu s předáním skutečného objektu.
6. Přístup k atributům JavaScriptového objektu z Go
Předpokládejme, že v prvním argumentu Go funkce volané z JavaScriptu je uložena hodnota typu Object. Pro zjednodušení programového kódu si tento objekt uložíme do pomocné proměnné nazvané taktéž object (v jazyce Go se nejedná o rezervované klíčové slovo):
object := args[0]
Nyní můžeme s využitím metody Value.Get() získat libovolný atribut objektu. Pokud atribut neexistuje, je vrácena hodnota typu undefined:
object.Get("firstName") object.Get("lastName") object.Get("age") object.Get("eyeColor") object.Get("somethingElse")
7. Demonstrační příklad: přečtení vybraných atributů předaného JavaScriptového objektu
V dnešním třetím demonstračním příkladu je ukázáno, jakým způsobem lze přečíst atributy (s předem známými jmény) z JavaScriptového objektu, který je předán do Go funkce. Go funkce nejdříve provede všechny testy, které jsme již viděli (počty a typy předaných argumentů) a následně pomocí metody Get() přečte hodnoty atributů:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintObject // - kontrola typu argumentu předaného funkci PrintObject // - přečtení atributů z předaného objektu package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintObject(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } object := args[0] // kontrola typu předaného argumentu typ := object.Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // přečíst a zobrazit atributy objektu fmt.Printf("First name: %v\n", object.Get("firstName")) fmt.Printf("Last name: %v\n", object.Get("lastName")) fmt.Printf("Age: %v\n", object.Get("age")) fmt.Printf("Eye color: %v\n", object.Get("eyeColor")) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintObject tak, aby byla volatelná // z JavaScriptu js.Global().Set("printObject", js.FuncOf(PrintObject)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
Příslušná HTML stránka, z níž je Go funkce zavolána a je jí předán objekt:
<!doctype html> <html> <head> <title>Function call: with object argument</title> </head> <body> <h1 id="header">Function call: with object argument</h2> <script src="func_call_object_2.js" type="text/javascript"></script> <script type="text/javascript"> // konstrukce objektu const person = new Object(); // přidání atributů person.firstName = "John"; person.lastName = "Doe"; person.age = 42; person.eyeColor = "blue"; // zavolání funkce naprogramované v Go s předáním objektu printObject(person); </script> </body> </html>
A takto by měly vypadat výsledky vypsané do konzole webového prolížeče:

Obrázek 3: Výsledek volání funkce printObject z JavaScriptu s předáním skutečného objektu.
8. Konverze atributů předaného objektu do nativních typů Go
Opět nyní předpokládejme, že do Go funkce byl předán JavaScriptový objekt v prvním argumentu. Můžeme si ho tedy uložit do pomocné proměnné:
object := args[0]
Atribut firstName by měl obsahovat řetězec, takže můžeme přímo provést jeho konverzi z js.Value na typ string. V tomto případě můžeme nejdříve provést i test, zda je atribut správného typu, popř. lze tento test vynechat a přímo psát:
firstName := object.Get("firstName").String()
Podobně je možné získat obsah atributu age ve formě celého čísla:
age := object.Get("age").Int()
typ := object.Get("age").Type() if typ != js.TypeInt { ... ... ... }
9. Demonstrační příklad: konverze všech atributů objektů do nativních typů jazyka Go
Ukažme si nyní, jakým způsobem se načtou všechny (předem známé) atributy objektu předaného do Go s následnou konverzí těchto atributů na nativní datové typy Go:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů funkci PrintObject // - kontrola typu argumentů předaných funkci PrintObject package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintObject(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } object := args[0] // kontrola typu předaného argumentu typ := object.Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // převést atributy do nativních typů jazyka Go firstName := object.Get("firstName").String() lastName := object.Get("lastName").String() age := object.Get("age").Int() eyeColor := object.Get("eyeColor").String() // zobrazit atributy objektu fmt.Printf("First name: %s (%T)\n", firstName, firstName) fmt.Printf("Last name: %s (%T)\n", lastName, lastName) fmt.Printf("Age: %d (%T)\n", age, age) fmt.Printf("Eye color: %s (%T)\n", eyeColor, eyeColor) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce PrintObject tak, aby byla volatelná // z JavaScriptu js.Global().Set("printObject", js.FuncOf(PrintObject)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
HTML stránka, ze které se volá zaregistrovaná Go funkce, vypadá takto:
<!doctype html> <html> <head> <title>Function call: with object argument</title> </head> <body> <h1 id="header">Function call: with object argument</h2> <script src="func_call_object_3.js" type="text/javascript"></script> <script type="text/javascript"> // konstrukce objektu const person = new Object(); // přidání atributů person.firstName = "John"; person.lastName = "Doe"; person.age = 42; person.eyeColor = "blue"; // zavolání funkce naprogramované v Go s předáním objektu printObject(person); </script> </body> </html>
Po načtení výše uvedené HTML stránky do webového prohlížeče by se v jeho konzoli měly zobrazit tyto řádky s atributy předaného objektu:

Obrázek 4: Výpis typů a hodnot atributů předaných do Go funkce z JavaScriptového kódu.
10. Předání pole, získání počtu prvků pole
V JavaScriptu lze vytvářet i pole, která se chovají jako měnitelné heterogenní kontejnery. Pole jsou měnitelná z toho důvodu, že prvky v poli je možné modifikovat a navíc lze do pole přidávat další prvky, popř. prvky odebírat (například metodami push, pop, shift a když víte, co děláte, tak i metodou delete). Pole jsou heterogenní proto, že každý prvek pole může být jakéhokoli typu. Nejjednodušší způsob konstrukce pole spočívá v použití literálu, který v případě polí znamená zápis hodnot prvků mezi hranaté závorky:
[] [1] [1, 2, 3] ["foo", "bar", "baz"] [true, false]
V případě heterogenního pole pak i:
[42, "is", true, "answer"]
Z pohledu typového systému programovacího jazyka JavaScript jsou pole objekty, které mají definovaných cca patnáct standardních metod a taktéž atribut length vracející délku pole. To znamená, že i v příslušné Go funkci budeme s takovým polem muset pracovat jako s objektem.
Jen pro úplnost si atribut i standardní metody pole vypišme:
Atribut/metoda |
---|
Array length |
Array toString() |
Array at() |
Array join() |
Array pop() |
Array push() |
Array shift() |
Array unshift() |
Array delete() |
Array concat() |
Array copyWithin() |
Array flat() |
Array splice() |
Array toSpliced() |
Array slice() |
11. Demonstrační příklad: předání pole do Go funkce se zjištěním jeho délky
Pro datovou strukturu js.Value je definována i metoda nazvaná Length, která v případě polí vrací jejich délku a v případě objektů jiných typů hodnotu vlastnosti length (samozřejmě v případě, pokud tato vlastnost existuje, mimochodem existuje i u řetězců). To znamená, že délku pole je možné v jazyce Go zjistit následujícím způsobem:
// získat atribut s délkou pole array := args[0] length := array.Length()
V demonstračním příkladu se zjistí délka pole, která se následně vypíše do webové konzole:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů funkci PrintArrayLength // - kontrola typu argumentů předaných funkci ArrayLength // - výpočet délky pole předaného do Go funkce package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintArrayLength(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } // kontrola typu typu předaného argumentu typ := args[0].Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // získat atribut s délkou pole array := args[0] length := array.Length() // zobrazit zprávu fmt.Printf("Array length = %d\n", length) // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce printArrayLength tak, aby byla volatelná // z JavaScriptu js.Global().Set("printArrayLength", js.FuncOf(PrintArrayLength)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
HTML stránka pro otestování chování:
<!doctype html> <html> <head> <title>Function call: array as argument</title> </head> <body> <h1 id="header">Function call: array as argument</h2> <script src="func_call_array_1.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty printArrayLength([]); printArrayLength([1]); printArrayLength([1, 2, 3]); printArrayLength(["foo", "bar", "baz"]) printArrayLength([true, false]); </script> </body> </html>

Obrázek 5: Výpis délek polí jištěných ve funkci naprogramované v jazyce Go.
Pozor ovšem na „děravé“ pole, které bude vracet počet prvků včetně „děr“ (tedy prvků s nespecifikovanou hodnotou):
arr = [1, 2, 3, 4]; delete arr[2]; printArrayLength(arr);
V tomto případě se zjistí a vypíše hodnota 4.
12. Přístup k prvkům JavaScriptového pole z Go
V případě, že je nutné v jazyce Go pracovat s prvky (obsahem) polí předaných z JavaScriptu, je nutné tyto prvky konvertovat postupně. To znamená, že nemáme k dispozici žádnou univerzální konverzní funkci (nebo sadu funkcí), která by například dokázala vrátit Go pole s prvky typu int nebo string. Takové konverzní funkce neexistují z toho důvodu, že pole mohou být v JavaScriptu heterogenní.
Přístup k prvkům polí tedy musí být proveden postupně, k čemuž použijeme metodu Index, která pro zadaný index (celé číslo) vrátí n-tý prvek pole typu js.Value. S tímto prvkem následně pracujeme jako s jakoukoli jinou JavaScriptovou hodnotou: můžeme zjistit jeho typ, provést konverzi atd.
Nejprve (po příslušných kontrolách, které již známe) získáme hodnotu argumentu předaného do Go funkce:
array := args[0]
Jedná se přitom o pole, takže přečteme jeho délku metodou Value.Length():
length := array.Length() // zobrazit zprávu fmt.Printf("Array length = %d\n", length)
Následně explicitně projdeme všemi prvky pole a zjistíme typ a hodnotu těchto prvků (a to včetně případných „děr“, které pole mohou obsahovat):
// projít prvky pole for i := 0; i < length; i++ { item := array.Index(i) fmt.Printf("Item #%d has type = %s and value %v\n", i, item.Type(), item) }
13. Demonstrační příklad: výpis typů a hodnot všech prvků JavaScriptového pole
V dalším demonstračním příkladu je ukázán průchod všemi prvky pole předaného z JavaScriptu do Go, přičemž pro každý prvek je vypsán jeho typ i hodnota:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci PrintArray // - kontrola typu argumentů předaných funkci PrintArray // - výpočet délky pole // - výpis prvků pole package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func PrintArray(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } // kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // získat atribut s délkou pole array := args[0] length := array.Length() // zobrazit zprávu fmt.Printf("Array length = %d\n", length) // projít prvky pole for i := 0; i < length; i++ { item := array.Index(i) fmt.Printf("Item #%d has type = %s and value %v\n", i, item.Type(), item) } fmt.Println() // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce printArray tak, aby byla volatelná // z JavaScriptu js.Global().Set("printArray", js.FuncOf(PrintArray)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
HTML stránka, ze které výše deklarovanou funkci printArray (resp. přesněji řečeno PrintArray) voláme, vypadá takto:
<!doctype html> <html> <head> <title>Function call: array as argument</title> </head> <body> <h1 id="header">Function call: array as argument</h2> <script src="func_call_array_2.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty printArray([]); printArray([1]); printArray([1, 2, 3]); printArray(["foo", "bar", "baz"]) printArray([true, false]); </script> </body> </html>
Podívejme se na výsledky vypsané do konzole webového prohlížeče:

Obrázek 6: Výpis prvků polí z jazyka Go.
Pro „děravé pole“, v níž chybí prvek s indexem 2, se vypíše:
Array length = 4 func_call_array_2.js:45:25 Item #0 has type = number and value <number: 1> func_call_array_2.js:45:25 Item #1 has type = number and value <number: 2> func_call_array_2.js:45:25 Item #2 has type = undefined and value <undefined> func_call_array_2.js:45:25 Item #3 has type = number and value <number: 4> func_call_array_2.js:45:25
14. Součet prvků předaného pole
Předpokládejme, že JavaScriptové pole předávané do funkce naprogramované v jazyku Go obsahuje pouze číselné hodnoty (a je jedno, zda celá čísla nebo čísla s plovoucí řádovou čárkou). Například:
arr = [1, 1.5, 2, 100, -10]
V případě, že budeme chtít sečíst všechny prvky tohoto pole, lze postupovat následujícím způsobem. Nejdříve získáme argument s polem a zjistíme počet prvků pole (ještě předtím je ovšem vhodné zkontrolovat typ argumentu):
// získat atribut s délkou pole array := args[0] length := array.Length()
Následně můžeme využít sekvenci operací array.Index(i), která získá jeden z prvků pole (typu js.Value), za níž následuje operace Value.Int(), jež hodnotu převede na nativní celočíselnou hodnotu jazyka Go (pozor na 32bitové platformy!). Mezitím je více než vhodné zkontrolovat typ prvku, protože již víme, že JavaScriptová pole mohou být heterogenní. Výpočet sumy je pak snadný:
for i := 0; i < length; i++ { item := array.Index(i) typ := item.Type() if typ != js.TypeNumber { fmt.Printf("Item #%d has incorrect type %s\n", i, typ.String()) return nil } value := item.Int() sum += value }
15. Demonstrační příklad: součet všech prvků pole předaného z JavaScriptu do Go
Výše uvedený postup pro výpočet součtu všech prvků pole je prakticky ukázán na následujícím demonstračním příkladu. Je část naprogramovaná v jazyce Go vypadá následovně:
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - kontrola počtu argumentů předaných funkci arraySum // - kontrola typu argumentů předaných funkci arraySum // - výpočet délky pole // - výpočet součtu prvků pole package main import ( "fmt" "syscall/js" ) // funkce, která se bude volat z HTML stránky, jakoby // se jednalo o JavaScriptovou funkci func ArraySum(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } // kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeObject { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } // získat atribut s délkou pole array := args[0] length := array.Length() // zobrazit zprávu fmt.Printf("Array length = %d\n", length) // projít prvky pole a vypočítat sumu sum := 0 for i := 0; i < length; i++ { item := array.Index(i) typ := item.Type() if typ != js.TypeNumber { fmt.Printf("Item #%d has incorrect type %s\n", i, typ.String()) return nil } value := item.Int() sum += value } fmt.Printf("Sum = %d\n", sum) fmt.Println() // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) // export funkce arraySum tak, aby byla volatelná // z JavaScriptu js.Global().Set("arraySum", js.FuncOf(ArraySum)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
V JavaScriptu se pokusíme funkci arraySum zavolat s prázdným polem, s polem obsahujícím celočíselné hodnoty, ale taktéž s polem, které obsahuje hodnoty jiného typu:
<!doctype html> <html> <head> <title>Function call: array sum</title> </head> <body> <h1 id="header">Function call: array sum</h2> <script src="func_call_array_sum.js" type="text/javascript"></script> <script type="text/javascript"> // korektní argumenty arraySum([]); arraySum([1]); arraySum([1, 2, 3]); arraySum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // nekorektní argumenty arraySum(["foo", "bar", "baz"]) arraySum([true, false]); </script> </body> </html>
Výsledek by měl po spuštění ve webovém prohlížeči vypadat následovně:

Obrázek 7: Součty prvků polí s celočíselnými prvky, popř. chyby u polí, které obsahují prvky odlišného typu.
16. Předání funkce naprogramované v JavaScriptu do Go
V úvodní kapitole jsme si řekli, že funkce jsou v JavaScriptu (ale i v jazyce Go) plnohodnotným datovým typem, takže je možné funkci předat jako argument do jiné funkce, popř. naopak získat funkci jako návratovou hodnotu z jiné volané funkce. V takovém případě mluvíme o funkcích vyššího řádu, to je však téma přesahující tento článek. Ukážeme si namísto toho, jakým způsobem lze do jazyka Go předat JavaScriptovou funkci (resp. referenci na ni) a jak tuto funkci z Go zavolat.
Ze strany JavaScriptu může celý problém vypadat následovně – nadeklarujeme funkci nazvanou foo a tu předáme do funkce nazvané callFunction. Přitom nevíme a ani nás nemusí zajímat, zda se jedná o JavaScriptovou funkci nebo o funkci naprogramovanou v Go a transpilovanou do JavaScriptu nebo spuštěnou v rámci WebAssembly:
<script type="text/javascript"> function foo(message) { window.alert(message); } callFunction(foo); </script>
17. Zavolání JavaScriptové funkce
Na straně Go tedy máme argument, který by měl být typu js.TypeFunction. To si můžeme velmi snadno ověřit:
// kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeFunction { ... ... ... }
Pokud je argument skutečně typu „javascriptová funkce“, můžeme tuto funkci zavolat a předat jí libovolné argumenty. Pro tento účel se používá metoda Invoke s hlavičkou:
func (v Value) Invoke(args ...any) Value
Příklad zavolání funkce, jejíž referenci jsme získali, a předání řetězce do této funkce:
function := args[0] function.Invoke("Called from Go!")
18. Demonstrační příklad: zavolání JavaScriptové funkce z jazyka Go
V dnešním posledním demonstračním příkladu je ukázán postup popsaný v předchozích dvou kapitolách. V Go části aplikace otestujeme, zda je předán argument typu „javascriptová funkce“ a pokud tomu tak je, tuto funkci zavoláme a předáme jí řetězec (ten je automaticky zkonvertován):
// Technologie WebAssembly a GopherJS // // - rozhraní mezi jazyky Go a JavaScript // - zavolání callback funkce package main import ( "fmt" "syscall/js" ) func CallFunction(this js.Value, args []js.Value) any { // kontrola počtu předaných argumentů if len(args) != 1 { fmt.Printf("incorrect number of arguments %d, but exactly one is accepted\n", len(args)) return nil } // kontrola typu předaného argumentu typ := args[0].Type() if typ != js.TypeFunction { fmt.Printf("Argument #0 has incorrect type %s\n", typ.String()) return nil } function := args[0] function.Invoke("Called from Go!") // je nutné vrátit nějakou hodnotu return nil } func main() { fmt.Println("started") c := make(chan bool) js.Global().Set("callFunction", js.FuncOf(CallFunction)) // realizace nekonečného čekání // (nutno provést při překladu do WebAssembly, ktežto // v případě použití GopherJS je možné hlavní funkci ukončit) <-c fmt.Println("finished") }
Na HTML stránce je nejdříve nadeklarována nová JavaScriptová funkce. Ta je následně předána do kódu naprogramovaného v Go:
<!doctype html> <html> <head> <title>Callback</title> </head> <body> <h1 id="header">Callback</h2> <script src="callback.js" type="text/javascript"></script> <script type="text/javascript"> function foo(message) { window.alert(message); } callFunction(foo); </script> </body> </html>
Po zobrazení HTML stránky se nejprve vytvoří funkce foo, následně je předána do Go a poté je odsud spuštěna. Výsledkem by měl být dialog zobrazený voláním window.alert, přičemž zprávou je řetězec předaný v Go:

Obrázek 8: Dialog vyvolaný nepřímo z jazyka Go.
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 |
17 | func_call_string.go | devátý demonstrační příklad: předání řetězce z JavaScriptu do Go | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_string.go |
18 | func_call_string.html | HTML stránka s kódem pro načtení devátého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_string.html |
19 | func_call_object1.go | desátý demonstrační příklad: předání objektu z JavaScriptu do Go | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object1.go |
20 | func_call_object1.html | HTML stránka s kódem pro načtení desátého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object1.html |
21 | func_call_object2.go | jedenáctý demonstrační příklad: přístup k atributům předaného objektu | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object2.go |
22 | func_call_object2.html | HTML stránka s kódem pro načtení jedenáctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object2.html |
23 | func_call_object3.go | dvanáctý demonstrační příklad: konverze atributů předaného objektu do nativních typů Go | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object3.go |
24 | func_call_object3.html | HTML stránka s kódem pro načtení dvanáctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_object3.html |
25 | func_call_array1.go | třináctý demonstrační příklad: předání pole se získáním počtu prvků | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array1.go |
26 | func_call_array1.html | HTML stránka s kódem pro načtení třináctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array1.html |
27 | func_call_array2.go | čtrnáctý demonstrační příklad: výpis prvků předaného pole | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array2.go |
28 | func_call_array2.html | HTML stránka s kódem pro načtení čtrnáctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array2.html |
29 | func_call_array_sum.go | patnáctý demonstrační příklad: součet prvků předaného pole | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array_sum.go |
30 | func_call_array_sum.html | HTML stránka s kódem pro načtení patnáctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/func_call_array_sum.html |
31 | callback.go | šestnáctý demonstrační příklad: předání funkce s jejím zavoláním | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/callback.go |
32 | callback.html | HTML stránka s kódem pro načtení šestnáctého demonstračního příkladu do webového prohlížeče | https://github.com/RedHatOfficial/GoCourse/blob/master/lesson12/callback.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