5

How can I call a function named apply() defined on a Javascript object from Scala.js code?

The obvious solution does not work since apply is always compiled into a function call on its parent object by Scala.JS (?):

var test = {
  apply: function(idx) {...},
  apple: function(idx) {...}
}
trait Test extends js.Object {
  def apple(idx: Int) : Int = ???
  def apply(idx: Int) : Int = ???
}

val test = js.Dynamic.global.test.asInstanceOf[Test]

test.apple(1) // works, of course

test.apply(1) // does not work (seems to be compiled into the call test(1) ?)

js.Dynamic.global.test.apply(1) // does not work either
jokade
  • 197
  • 5
  • A slight aside, but if possible avoid calling a function `apply` unless you definitely want to overwrite the default `apply` behaviour: http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply – glenatron Aug 19 '14 at 10:11
  • thanks for the hint; however, I don't want to call apply() on a function, but on an object. – jokade Aug 19 '14 at 10:31
  • In JavaScript an object _is_ a function. Pop up your favourite console and type `Object instanceof Function` if you're not sure. Of course, the reverse is _also_ true. Expecting JavaScript to behave like a classical Object Oriented language will result in confusion sooner or later. http://stackoverflow.com/questions/7018023/is-every-javascript-object-a-function – glenatron Aug 19 '14 at 10:42
  • well, the console in Chrome replies with "TypeError: object is not a function" when I do this: `test = { apply: function() {} }; test()` – jokade Aug 19 '14 at 10:57
  • Here `test` is an anonymous object - which is indeed not a function - but also cannot be instantiated or called because you have declared it as a variable rather than an object. So you can call `test.apply()` but test is the name of the variable not the name of a type. – glenatron Aug 19 '14 at 12:05

1 Answers1

5

You can annotate the apply method in your facade type with @JSName("apply"). This will yield the desired behavior:

trait Test extends js.Object {
  def apple(idx: Int) : Int = ???
  @JSName("apply")
  def apply(idx: Int) : Int = ???
}

Testing:

val test = js.Dynamic.literal(
    apply = (idx: Int) => idx,
    apple = (idx: Int) => idx
).asInstanceOf[Test]

test.apple(1) // -> 1
test.apply(1) // -> 1

For the dynamically typed case, you'll have to invoke applyDynamicNamed manually:

val dyn = test.asInstanceOf[js.Dynamic]

dyn.applyDynamicNamed("apply")(1) // -> 1
gzm0
  • 14,752
  • 1
  • 36
  • 64