I have a controller that looks like this:
function controller($scope)
{
this.helper() = function()
{
// some processing
};
$scope.doSomething = function()
{
helper();
};
}
When doSomething is called, I get an error saying that helper() is not defined. Putting 'this' in front of the call to helper() doesn't work either as 'this' here refers to $scope and not to the controller instance.
My question is: is there a way to call such local helper functions from within a function on the scope? (I know I could just put helper() on the $scope too but would rather not as it's strictly a convenience function, not something to be called from the view.)
The reason for structuring the code like this is to facilitate separate testing of the helper() function from a unit test.
function controller($scope)
{
this.helper() = function()
{
// some processing
};
var helperScope = this;
$scope.doSomething = function()
{
helperScope.helper();
};
}
Of course, give it a more descriptive name than that, but the basic idea is to assign this to a variable within the scope you want.
Related
In my knockout.js project I wrote some self invoking functions like this:
var addMarkers = function () {
ko.utils.arrayForEach(self.sectionList(), function (sectionItem) {
ko.utils.arrayForEach(sectionItem.placeList(), function (placeItem) {
placeItem.marker.addListener('click', function () {
map.panTo(placeItem.marker.getPosition());
});
});
});
}();
The function works without problems, however in JSLint the "var addMarkers" was highlighted as unused variable. That makes me wonder if I should the function like this, or just make anonymous because it is a better practice?:
function addMarkers (){ code to be executed };
Assigning the result of a self-executing function is often useful. In particular, I like to define my main viewmodel this way, because
I don't need a prototype for my viewmodel
I'm not likely to need more than one instance of a viewmodel
The reason you would use a self-executing function is that you need a local scope for whatever you're doing, to isolate it from surrounding scope. The reason you would assign a variable from it is that you want it to return a value you will use later. In your example, neither of these is true.
If I have a function as follows (pseudocode) :
function doSomething (input) {
input.something(something);
do(input, function (res) {
input.send(res)
}
}
However, I want to be able to run it from another area, and the only shared variable between the two is $scope, so I do something like this:
$scope.doSomething = function (input) {
doSomething(input);
}
Now, I can call doSomething using $scope. However, creating an extra function to do it is quite messy. Ideally, I'd like to be able to do something like this:
$scope.doSomething = doSomething(input);
That doesn't work though. Is there a similar shorthand you can use to create a reference to a function from another, without the need for a 'useless' function?
You should be able to do that if you've already declared the function. But instead of using doSomething(input) which is actually calling the function, use it without the brackets like so
$scope.doSomething = doSomething;
what you need to do is assign function pointer.
$scope.doSomething = doSomething;
what you are currently doing $scope.doSomething = doSomething(input); is assigning function call result, rather than pointer
I'm currently trying to implement some common JS concepts
in little projects to understand better how to use them.
I've been working on a simple game, trying to
understand and use the module pattern and closures.
I'm using the module pattern from Stoyan Stefanov's 'patterns'
book.
I'm struggling to understand how best to mix modules and
closures.
I'd like to know if I'm organising the following code in a
sensible way? If so, my question is: what's the best way
to modify the code so that in the $(function(){}) I have
access to the update() function?
MYAPP.utilities = (function() {
return {
fn1: function(lives) {
//do stuff
}
}
})();
MYAPP.game = (function() {
//dependencies
utils = MYAPP.utilities
return {
startGame: function() {
//initialisation code
//game state, stored in closure
var lives = 3;
var victoryPoints = 0;
function update(){
utils.fn1(lives);
//do other stuff
}
}
}
})();
$(function(){
MYAPP.game.startGame();
//Want to do this, but it won't work
//because I don't have access to update
$('#button').on('click',MYAPP.game.update)
});
I've come up with a couple of options which would work, but
I'd like to know if they're good practice, and what the best
option is.
Options:
(1) Bind $('#button').on('click', ...) as part of the
startGame initialisation code.
(2) Assign the update() function to a variable, and
return this variable from the startGame function, So in
$(function(){}) we could have
updatefn = MYAPP.game.startGame(); and then
$('#button').on('click',MYAPP.game.update)
(3)? Is there a better way?
Thank you very much for any help,
Robin
First off, to access the update function in that fashion it will have to exposed in the returned object.
return {
update: function() {
[...]
},
startGame: function() {
[...]
this.update();
}
}
Calling obj.method() automatically sets the this reference inside this method call to obj. That is, calling MYAPP.game.startGame() sets this to MYAPP.game inside this startGame method call. More details about this behavior here.
You will also want to move the lives variable to a common scope which is accessible by both startGame and update methods, which is exactly what the closure is for:
MYAPP.game = (function() {
[...]
var lives; //private/privileged var inside the closure, only accessible by
//the returned object's function properties
return {
update: function() {
utils.fn1(lives);
},
startGame: function() {
[...]
lives = 3; //sets the closure scope's lives variable
[...]
this.update();
}
}
})();
Fiddle
In this case you will need some method to set the lives variable when you want to change it. Another way would be to make the lives variable public as well by making it a property of the returned object and accessing it through this.lives inside of the methods.
NOTE: If you simply pass a reference to the function object stored as property of the returned object as in:
$('#button').on('click', MYAPP.game.update);
The this reference inside the click handler will not point to MYAPP.game as the function reference that has been passed will be called directly from the jQuery core instead of as an object's member function call - in this case, this would point to the #button element as jQuery event handlers set the this reference to the element that triggered the handler, as you can see here.
To remedy that you can use Function.bind():
$('#button').on('click', MYAPP.game.update.bind(MYAPP.game));
Or the old function wrapper trick:
$('#button').on('click', function() {
MYAPP.game.update(); //called as method of an obj, sets `this` to MYAPP.game
});
This is important when the this keyword is used inside the update method.
There are a few issues in your code. First, update() function is not visible outside the object your creating on the fly. To make it part of game object it has to be on the same level as startGame.
Also, if you declare var lives = 3 it will be a local variable and it won't be visible outside startGame() function, as well as victoryPoints. These two variable have to be visible in some way (via closure or as object fields).
Finally, attaching MYAPP.game.update as an event listener will attach just that function, preventing you from using all other object methods/functions. Depending on what you want to do you might prefer to pass a closure like function() { MYAPP.game.update() } instead.
Your code should look something like:
MYAPP.utilities = (function() {
return {
fn1: function(lives) {
console.log(lives);
}
}
})();
MYAPP.game = (function() {
//dependencies
utils = MYAPP.utilities
var lives;
var victoryPoints;
return {
startGame: function() {
//initialisation code
//game state, stored in closure
lives = 3;
victoryPoints = 0;
},
update: function() {
utils.fn1(lives);
//do other stuff
}
}
})();
$(function(){
MYAPP.game.startGame();
//Want to do this, but it won't work
//because I don't have access to update
$('#button').on('click', MYAPP.game.update)
});
(DEMO on jsfiddle)
What I have is something like that:
jQuery(function($) {
'use strict';
var App = {
init: function() {
App.startTool();
}
[...]
and when I try to call App.init(); from another file it say that App is not defined.
I'm trying to create some test with jasmine and I've the same error.
How can I go inside this "literal class", nested inside a simple function, from external files?
Javascript has function scope. This means that variables defined within a function are only visible within that function. To access this variable from outside the function, you'll need to either declare it outside the function, attach it to the window or some other global object directly, or return it as a value from the function.
Declaring outside:
var App;
jQuery(function($) {
'use strict';
App = {
init: function() {
App.startTool();
}
[...]
Attaching to the window or other global namespace:
jQuery(function($) {
'use strict';
window.App = { //or namespace.App where namespace can be another global variable.
init: function() {
App.startTool();
}
[...]
The way you're wrapping it you're not going to be able to return the value, but if it was a different function you could do this:
var App= (function() {
'use strict';
var App = {
init: function() {
App.startTool();
}
return App;
}())
A little more on function scope: Variables declared within a function cannot be seen from outside that function. Variables declared outside a function can be seen from inside a function. So if you want an object to be global, you need to declare it outside any function, or set it as a property on something that already has been declared outside the function. In a browser, the window object is global by default. Other environments like nodejs or rhino will have their own global objects.
Its important to understand how JS scope works because its the foundation behind a lot of the more powerful features of the language, particularly closures and the module pattern.
Some people have also mentioned the benefits of namespacing. This is a good point for this context. Having a single global variable for a library or application allows you to avoid conflicts with other libraries or scripts you might be using. You can then attach your other variables to that global namespace variable and reference them as properties of that object. So instead of calling App.init directly, call myProgram.App.init() for instance.
If it is not exposed as a global than you can not touch it.
You would have to put it into some namespace that is in the global scope.
jQuery(function($) {
'use strict';
var App = {
init: function() {
App.startTool();
}
}
if (!window.myNamespace) {
window.myNamespace = {};
}
myNamespace.App = App;
});
The fun thing here is it will not exist until document.ready, not sure why you would want it wrapped with ready. The init call should be called on ready. So you are doing to have race conditions on what widget registers first.
You need to make App globally available if you want to use it from outside the function:
jQuery(function($) {
'use strict';
// attach it to window instead of using var
window.App = {
init: function() {
App.startTool();
}
};
});
I have a Javascript Object structured after the Module Pattern. I have several private function in it which are called from other sibling "private" functions. How can I access another variable/function without the potential to accidentally access a global/external variable/object/function?
function doSomething() {
alert("Something I don't want to do");
}
var My.Namespaced.SingletonClass = (function() {
var init = function() {
doSomething();
}
var doSomething = function() {
alert("Something I want to do");
}
return {
"init": init;
}
})();
My.Namespaced.SingletonClass.init();
My guess is that the above code would in fact access the correct, inner doSomething function, but I'd like some more security than that. How can I explicitly address the inner/nested function without fear of accidentally calling functions or addressing objects in the scope around my singleton?
Short version: you can't. If doSomething isn't defined as a sibling of init, then JavaScript will search successively broader scopes until it finds a doSomething function, or it runs out of scopes to search.
Longer version: you can prevent this sort of behavior by using a private object to hold your private helper functions, like this:
function doSomething() {
alert("Something I don't want to do");
}
// Assuming My.Namespaced is already defined:
My.Namespaced.SingletonClass = (function() {
var helpers = {};
helpers.doSomething = function() {
alert("Something I want to do");
}
var init = function() {
helpers.doSomething();
}
return {
init: init
}
})();
My.Namespaced.SingletonClass.init();
I'm not sure if it's important that the helper functions are truly siblings (but I don't see why that would particularly matter).
Also keep in mind that My and My.Namespaced need to be defined before you tack on SingletonClass - and there's no need to use JSON-style quoting for keys in the object you're returning.