I have following CS code snippet:
class Ctrl
constructor: (@security) ->
...
isAuthenticated: -> @security.isAuthenticated()
which is translated to following JS:
Ctrl = (function() {
function Ctrl(security) {
this.security = security;
...
}
Ctrl.prototype.isAuthenticated = function() {
return this.security.isAuthenticated();
};
})()
As you can see isAuthenticated
is a simple delegation to security
object's method and creating anonymous function is redundant.
I want to avoid creating this additional call level and instead perform kind of 'inline delegation' which would translate to JS similar to:
Ctrl = (function() {
function Ctrl(security) {
this.security = security;
...
}
Ctrl.prototype.isAuthenticated = this.security.isAuthenticated;
})()
Following doesn't work, since it tries to bind @security
to wrong object:
class Ctrl
constructor: (@security) ->
...
isAuthenticated: @security.isAuthenticated
Any clues ?
You can hook up delegation in various ways but you have to be aware of two things:
@
is when you're hooking up the delegation.@security.isAuthenticated
probably won't work if isAuthenticated
is called with a this
that isn't @security
.(1) tells you what you need to attach the delegate functions to (see below). (2) is the usual "a function reference isn't really a method" problem in JavaScript; for example:
o =
m: -> console.log(@)
o.m() # Puts `o` in the console
f = o.m; f() # Puts `window` (usually) in the console.
So you have to keep isAuthenticated
bound to @security
or it probably won't work.
One simple way to add delegation is to attach a bunch of bound functions to @
inside the constructor
:
delegate = (to, from, methods...) ->
for m in methods
to[m] = from[m].bind(from)
class Ctrl
constructor: (@security) ->
delegate(@, @security, 'isAuthenticated', 'somethingElse')
Then you can say:
c = new Ctrl(s)
c.isAuthenticated()
c.somethingElse(11)
and the expected things happen. Demo: http://jsfiddle.net/ambiguous/we8gT/
One problem with that approach is that each instance of Ctrl
ends up with its own isAuthenticated
and somethingElse
functions: they're not attached to the prototype so they're not shared.
But we can always create our own functions and take advantage of the fact that we can call code inside class C
:
delegate = (klass, property, methods...) ->
for m in methods
do (m) -> klass::[m] = (args...) -> @[property][m](args...)
class Ctrl
delegate(@, 'security', 'isAuthenticated', 'somethingElse')
constructor: (@security) ->
A few things of note:
::
is used to access the prototype so the methods will be shared.(args...) ->
to pass any arguments through to the @security
method.@
at the class
level is the class itself.do
to ensure that m
is what we expect it to be when the delegated function is called.Demo: http://jsfiddle.net/ambiguous/4c87J/
You can do delegation but you don't get it for free.
You cannot use an instance's .security
object on the prototype. You need to create the isAuthenticated
method in the constructor where you have access to it:
class Ctrl
constructor: (@security) ->
@isAuthenticated = security.isAuthenticated
…
(translate)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With