Caspert Caspert - 4 months ago 15
jQuery Question

How to use controller-scoped version of this to refer to the current controller in JS?

I ran into the following problem. My JavaScript structure is like the following.

I created an object that contains all the controllers. Those controllers have their own responsibilities. Below code belongs to the

main.js
file that is called first:

main.js

var App = {};

App.init = function() {

console.log('init');

App.uiController.init();
App.heroController.init();

}


Within the function
init()
I call the controllers' initializers.

A controller object looks like the following:

uiController.js

App.uiController = {

root: 0,
init: function() {

// Development
console.log('init uiController');

root = this;

// Call functions
root.doSomething();

},

doSomething: function() {

}
}


After all the scripts are loaded, I call the last script
init.js
:

init.js

App.init();


My HTML markup looks like the following :

<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
</head>

<body>

<!-- // Start HTML \\ -->
<header id="header">
</header><!-- End header#header -->

<main class="main">
</main><!-- End main.main -->

<footer id="footer">
</footer><!-- End footer#footer -->



<!-- JavaScript -->
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script src="scripts/main.js" type="text/javascript"></script>

<script src="scripts/constructors/uiController.js" type="text/javascript"></script>
<script src="scripts/constructors/heroController.js" type="text/javascript"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="scripts/init.js" type="text/javascript"></script>

</body>
</html>


Within the functions I use
root
to select
this
- that file where I currently been. Normally I create the variable
root
in every function (scoped) and give it the value
this
. But I thought this could be better, to create in every controller object one empty root (globally), so I've to only create and add a value once to
root
, like the following:

App.uiController = {

root: 0,
init: function() {

root = this;

// Call functions
root.doSomething();

},

doSomething: function() {

}
}


When I console.log
root
within the
init()
function, it will return an object with all the function that lives within the current controller where I am now, in above example
App.uiController
. So I thought this was working great and I've to create and declare
root
only once for each controllers. So I did the same for
heroController
as I did for
uiController
and log the variable
root
to the console. I receive to different objects with the right functions in it:

enter image description here

But, here comes the problem. In
main.js
I called
heroController.init()
after
uiController.init()
and When uiController is triggered by an event within the controller, I receive errors in my console that some functions within the
uiController
not working as expect.

When I remove the global
root
variable inside each controller and declare it in the init controllers like
var root = this;
the code will work again.

Does anyone know why the above setup doesn't work as thought?

Looking forward to your response.

UPDATE



Based on the answer from Kingshuk basak, I tried some new things. Here are my thoughts:

The answer code is not working for me. It will give me the error:
Uncaught ReferenceError: root is not defined
. When I use the code from my question and change the
root: 0
of the
heroController
to
1
and log both to the console:
console.log(App.uiController.root, App.heroController.root);
I get
0
and
1
in the console. It doesn't matter where I place it in both codes, the values will not be overridden by the other ones. This makes it really strange to me.

Then I tried also comment out
heroController
in
main.js
, then console.log
root
in the
uiController.init() function
. The value that I get back is
0
and that's true, but after that I set
root = this;
and console.log directly after it
root.root
the value will be also
0
and not
this
as the
uiController
object. Which makes it even more confusing, why
root
isn't changing to the object instead of
0
. Also when there is just one
root
in the hole code, after I comment out
heroController.init()
in the file
main.js
. Console.log only
root
will give me the
uiController
object as expected.

Last, I tried also inside both
init()
functions this:

root = this;
this.root = root;
console.log('heroController', root);


When I do this, the value of
root
is for each controller correct, because it will contain the object - this object - with all the corresponding functions in it. But this will leave me with the same problem as mentioned before, that uiController is not working as expect.

As you can see, both roots inside each controller as different functions

Hope this gives everybody a lot more information about the status and maybe some help for a nice solution!

Answer

Summary

Setting root as a property of your controller in order to save you from having to type var root = this in your controller methods is not useful. You should just do var root = this when needed because the property ultimately is not a net benefit over var root = this.

Explanation

The first answer you got is a partial answer. kingshuk basak is right that what you are doing is setting a root variable in the global space. See, if you do this:

function foo() {
  something =  1;
}

you are setting something in the global space. To make it local to your function you need var:

function foo() {
  var something =  1;
}

The lack of var in the first snippet does not make JavaScript interpret it as this.something, even if the function is part of a prototype.

kingshuk basak suggesting you make it part of your controller by using this. This works, yes but then you have to access it using this because that's how JavaScript works. So the naive way to use it would be:

uiController = {
  root: 0,
  init: function() {
    this.root = this;
    this.root.doSomething(); // You havve to use `this` here too!!
  },

  doSomething: function() {
     this.root.somethingElse();
  }
}

From what your question says, you are just trying to save yourself from having to write var root = this. So root won't ever change. But think about this for a second.

What you are doing is adding a property named root to this, which has the exact same value as this. In order to access root, which has the same value as this, you already need to have this. So there's no good reason for this.root to exist. As explained above, if you do this.root = this, then each access to it must be this.root so you've not appreciably reduced your burden. Not only that, but doing this.root.foo() requires an additional indirection at execution: root has to be fetched from this first.

You could do this:

uiController = {
  root: 0,
  init: function() {
    var root = this.root = this;
    root.doSomething();
  },

  doSomething: function() {
     var root = this.root;
     root.somethingElse();
  }
}

But again, this does not reduce the burden.

You should just do var root = this when you need it.