Go: syscall/js: possibility of using String.prototype.* methods on strings

Created on 1 Dec 2019  路  5Comments  路  Source: golang/go

With GopherJS, it was possible to call the String.prototype.toLowerCase method on a JavaScript String:

https://gopherjs.github.io/playground/#/ekZpm7tuW4

It's possible I'm overlooking something trivial, but this doesn't seem possible with syscall/js API of WebAssembly. Consider the same program modified to use syscall/js:

package main

import (
    "fmt"
    "syscall/js"
)

func main() {
    body := js.Global().Get("document").Get("body")

    fmt.Println(body.Get("nodeName"))
    fmt.Println(body.Get("nodeName").Call("toLowerCase"))
    fmt.Println(body.Get("nodeName").Get("toLowerCase").Invoke())
}

Its output with Go 1.13.4 is:

panic: syscall/js: call of Value.Get on string

goroutine 1 [running]:
syscall/js.Value.Get(0x7ff800010000000e, 0x34d12, 0xb, 0x7ff800010000000e)
    /usr/local/go/src/syscall/js/js.go:252 +0x39
main.main()
    /tmp/tmp.CQimpW7L/main.go:12 +0xb

Depending on whether a JavaScript String type is considered a "JavaScript object" or not, this may be consistent with js.Value.Get documentation, which says:

Get returns the JavaScript property p of value v. It panics if v is not a JavaScript object.

I'm wondering if it's possible to use toLowerCase with syscall/js API? If not, should it be possible?

/cc @neelance

Arch-Wasm NeedsInvestigation

Most helpful comment

This is because of JavaScript's autoboxing. string and String are two distinct types. The first is the primitive type, the second one is the boxed object type and only the second has methods. Autoboxing makes it so the expression

"foo".toUpperCase()

gets interpreted as

new String("foo").toUpperCase()

I do not yet see why syscall/js should emulate autoboxing. Is there any proper use case? In my opinion, for the examples above the proper solution is to use the .String() method to get the Go string and then use Go's functions for manipulating the string.

All 5 comments

Workaround: you can call JS String methods via the String.prototype itself, i.e. in JS:

String.prototype.toLowerCase.call("FOO")

In Go:

js.Global().Get("String").Get("prototype").Get("toLowerCase").Call("call", js.ValueOf("FOO"))

I have used this in gopherjs/vecty#251 in order to workaround this problem.

If not, should it be possible?

I think it should. The current API relies on Reflect.get which only works on objects, and therefore fails on normal strings. With a little bit of code, and now with the argument spread syntax available, I was able to make it work.

fmt.Println(body.Get("nodeName").Call("toLowerCase"))
fmt.Println(body.Get("nodeName").Call("charAt", 3))

Not pasting the code here. But I can send a CL if Richard is okay with it.

This is because of JavaScript's autoboxing. string and String are two distinct types. The first is the primitive type, the second one is the boxed object type and only the second has methods. Autoboxing makes it so the expression

"foo".toUpperCase()

gets interpreted as

new String("foo").toUpperCase()

I do not yet see why syscall/js should emulate autoboxing. Is there any proper use case? In my opinion, for the examples above the proper solution is to use the .String() method to get the Go string and then use Go's functions for manipulating the string.

Thanks for the explanation.

I do not yet see why syscall/js should emulate autoboxing.

I agree, I don't think that would be a good change.

In my opinion, for the examples above the proper solution is to use the .String() method to get the Go string and then use Go's functions for manipulating the string.

It should also be possible to use JavaScript's toLowerCase explicitly, if one wants to (e.g., to avoid having to import strings package, or for other JavaScript functions where there isn't equivalent Go functionality).

It sounds like it is possible with one of these two ways:

var str string = "FOO"

js.Global().Get("String").New(str).Call("toLowerCase")

// or

js.Global().Get("String").Get("prototype").Get("toLowerCase").Call("call", str)

So nothing needs to be done. I'll close this if there aren't objections.

It looks like there are no objections. ping @dmitshur.

Was this page helpful?
0 / 5 - 0 ratings