2

I am porting this JavaScript function to Scala.js:

once: function (el, type, callback) {
  var typeArray = type.split(' ');
  for (var i = typeArray.length - 1; i >= 0; i--) {
    el.addEventListener(typeArray[i], function(e) {
      e.target.removeEventListener(e.type, arguments.callee);
      return callback(e);
    });
  };
},

Here is my attempt at writing the Scala code:

def once(element: TopNode, tpe: String, callback: Function1[Event,Any]): Unit = {
  tpe.split(" ").foreach(item => element.addEventListener(item, (e : Event) => {
    e.target.removeEventListener(e.`type`, ?) // <-- what do I put here?
    callback(e)
  }))
}

How can I reference my lambda in that place holder?

1 Answer 1

4

Scala.js does not have an equivalent of the arguments.callee of JavaScript. More generally, it does not have an equivalent of arguments. So a lambda cannot read a reference to itself, unless it is given to it via its captured environment. This can be achieved by storing the lambda in a val in the once method. Ideally, one would like to write this:

def once(element: TopNode, tpe: String,
    callback: Function1[Event,Any]): Unit = {
  val cb: js.Function1[Event, Any] = { (e: Event) =>
    e.target.removeEventListener(e.`type`, cb) // using cb here
    callback(e)
  }
  tpe.split(" ").foreach(item => element.addEventListener(item, cb))
}

However, this won't compile, because you cannot use a val (here cb) in the right-hand-side of its definition, even if that happens to be inside a lambda. Trying to do so results in the following compile error:

Main.scala:17: error: forward reference extends over definition of value cb
      e.target.removeEventListener(e.`type`, cb) // using cb here

There is a simple solution, though: use a lazy val instead:

def once(element: TopNode, tpe: String,
    callback: Function1[Event,Any]): Unit = {
  lazy val cb: js.Function1[Event, Any] = { (e: Event) =>
    e.target.removeEventListener(e.`type`, cb) // using cb here
    callback(e)
  }
  tpe.split(" ").foreach(item => element.addEventListener(item, cb))
}

Make sure to declare cb as a js.Function1, not a Function1. Otherwise cb itself would refer to the Scala function, but not the JS function resulting from the automatic conversion (which have different identities). You have to make sure cb refers to the JS function.

Fiddle: http://www.scala-js-fiddle.com/gist/2b848e2f01e7af522dc1

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.