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:
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.
Why does the above setup not work as I expect?
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.
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
.
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.
The reason is simple. The 'root' variable of the uiController is overWritten by the heroController as both are declared in the global namespace. Try different names as root1 and root2 for uiController and heroController, it will work fine.
Note : Ideally you should not pollute the global namespace to avoid such problems. Use closures if necessary.
This is what you have to do :
uiController = {
root: 0,
init: function() {
this.root = this;
// Call functions
root.doSomething();
},
doSomething: function() {
}
}
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