Original Question
Can't figure out why I can't call the second function from within that first function. I am using jQuery-turbolinks. (Also, if you happen to know of a better way to only run page-specific javascript in rails, let me know. Currently this is my best implementation where I check if the body has a certain class, and if it does then I run the init function within this javascript object).
app/assets/javascripts/blogs.js
$(document).ready(function(){
var blogsjs = {
myBlog: this,
init: function(){
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
myBlog.another();
});
});
},
another: function(){
alert("I was called!")
}
};
if($('body').hasClass("blogs") == true){
blogsjs.init();
}
});
Solution After Feedback
Simply Just used object.method() syntax from within a method to call another method within that same object:
$(document).ready(function(){
var blogsjs = {
init: function(){
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
blogsjs.another();
});
});
},
another: function(){
alert("I was called!");
blogsjs.yetanother();
},
yetanother: function(){
alert("yet another called");
}
};
blogsjs.init();
});
I don't like how messy this code looks, but the encapsulation benefits from an Object-oriented design, I think, is solid: Each resource's javascript only has access to the methods inside its javascript object.
I don't know what you're trying to accomplish with this part of your declaration:
var blogsjs = {
myBlog: this
}
but, this will NOT be set to blogsjs. It will be whatever it was in the above function. In Javascript, this is only set on a function call. It is NOT set in a Javascript literal declaration so you cannot statically declare a property that refers to the object itself. Javascript just does not support that.
You can add properties after the object is constructed that contain references to the object if desired.
If you want myBlog to be initialized to point to blogsjs, then you will have to do that after the object is defined:
var blogsjs = {
init: function() {...},
another: function() {...}
};
blogsjs.myBlog = blogsjs;
In addition, this line of code won't work:
myBlog.another();
because myBlog is a property of an object, not a variable. It must be referenced with its parent object.
So you're probably getting an Cannot read property 'another' of undefined exception because you're specifying myBlog on the blogsjs object but do not reference it. Also myBlog will not be a reference to blogsjs but the scope jquery calls the document.ready function with.
You need to either create the reference inside your init method:
init: function(){
var myBlog = this;
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
myBlog.another();
});
});
}
or simply use blogsjs from one scope above your init method.
Have a look at this question to learn about scoping.
Related
Is this possible? I'm trying to overwrite a javascript method after my page has been loaded. The code in question looks similar to this:
myObject = Backbone.ViewManager.BaseView.extend({
myMethod: function() {
alert("in old method definition");
},
initialize: function() {
var a = this;
Our.Events.on("alertEvent", function(){
a.myMethod();
}
}
);
(The Backbone.ViewManager bit is just a way to create an object in javascript using a framework and not important here.)
Note the event callback defined above in the initialize method. We initialize the objects as soon as they're created.
After my page has loaded I tried to redefine myMethod to alert a different message. But when the alertEvent fires the original message appears in the alert.
I assume this is because of a closure, that redefining the method on myObject after its been initialized won't affect the definition pointed to by a? If that's the case, is there something I can do to change the definition used by a, or am I out of luck?
You should be able to define myMethod on the object you create which will override the prototype myMethod. What you have written should be able to work because you arn't referencing myMethod directly, but through the a object.
The following works for me
function ObjCstr(){}
ObjCstr.prototype.myMethod = function(){ alert("Old Message"); };
ObjCstr.prototype.callMyMethod = function(){
var a = this;
return function(){ a.myMethod(); };
};
var test = new ObjCstr();
var fakeListener = test.callMyMethod();
fakeListener(); // "OldMessage"
test.myMethod = function(){ alert("Overridden!"); };
fakeListener(); // "Overridden!"
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)
How to attach a function dynamically to a javascript object.For ex: if the function for dynamic attachment is attach(),then i should be able to attach the function fn to onject obj as follows..
attach(
obj,fn,{
alert(1)
}
)
function attach(obj,fnName,code)
{
obj[fnName] = code;
}
If by "attach a function dynamically to a javascript object" you mean "add a function-object as an object property" then the syntax you've already shown is almost right. This is what it should be:
var fnName = "testFunc";
obj[fnName] = function() { alert("Test function"); };
// or
obj.testFunc = function() { ... };
// or
obj[fnName] = nameOfFunctionDefinedElsewhereInCurrentScope;
Which means you could call your attach() function like this:
// attach an anonymous function:
attach(obj, "newFunctionName", function() { alert(1); });
// attach a function defined elsewhere
attach(obj, "newFunctionName", someFunction);
Note: the attach() function really doesn't save any effort at all, in fact it just gives you more characters to type...
By the way (but don't do this), if the parameter you want to pass as code is a string of code do this:
var code = "alert(0);";
obj[fnName] = new Function(code);
More information: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function
EDIT : The other post's Function(code) solution appears better. I did not know about that constructor.
A possible solution may be:
Object.prototype.attach = function(name,code) {
this.name = function() {
eval(code);
};
}
You can attach them as function objects if they've already been defined, such as here: Javascript: better way to add dynamic methods?
You can also use the new Function constructor to dynamically define the functions, such as here: Creating functions dynamically in JS
Here is an explanation of the differences between eval and the new Function constructor: Are eval() and new Function() the same thing?
As a warning, use of eval() and the new Function constructor have created controversy and have been condemned (to some extent) by a number of individuals, such as here: Legitimate uses of the Function constructor
Here is more information about eval: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Here is more information about the new Function constructor: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
Assuming that definition of function attach, you should call it like this:
attach(obj, fnName, function(){ alert(1); });
The way you invoked it is invalid syntax.
Also, as you may have noticed, that's not a very useful function, since you can do the same thing using your one-line function definition:
obj[fnName] = function(){ alert(1); });
I'm trying to call a function within an object literal that I created, using the this keyword. But an error shows up saying this.doTheMove() is not a function:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
}
var Animation = {
init: function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
},
doTheMove: function(){
alert('Animation!');
}
}
Why is there an error?
An explanation of what's happening. Pointy's answer is good but I want to explain it more generically. A very good research on this can be found here
An event handler is just a callback. You pass it a function and an event to listen on. Interally all it will do is call that function.
Animation.init is just a getter for that function. Think of it like this:
var callback = Animation.init
animBtn.addEventListener('click', callback, false);
...
// internal browser event handler
handler() {
// internal handler does stuff
...
// Oh click event happened. Let's call that callback
callback();
}
So all you've done is passed in
var callback = function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
}
By default in javascript this === window. This will refer to the global object if it isn't set to something. The net effect is that window.doTheMove is called. And that function doesn't exist.
In this case since callback is actaully called by an event handler the this object points at the DOM object that triggered the event so your calling node.doTheMove which still doesn't exist.
What you wanted to do is wrap it with a reference to Animation.
var callback = function() {
Animation.init();
}
This is a function execution and it executes init on Animation. When you execute it on an object like that then internally this === Animation as you would expect.
To sum up. The issue here is that Animation.init is just a reference to a function. It has no information about anything else like Pointy mentioned.
You have to change the way you set that up:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', function() { Animation.init(); }, false);
}
In JavaScript, the fact that a function happens to be defined as part of an object literal really doesn't mean very much (if anything, in fact). The reference to Animation.init does get you to the proper function, but the problem is that when the function is later invoked (in response to an actual "click"), the browser calls the function but has no idea that the object "Animation" should be the this reference. Again, the fact that the function was declared as part of the object is of no importance at all here. Therefore, if you want this to be something in particular of your own choosing, then you have to make sure it's set explicitly in code you control. The solution above is about the simplest way to do it: it handles the "click" events with an anonymous function that does nothing other than invoke the "init" function via an explicit reference through "Animation". That will ensure that this refers to the "Animation" object when "init" runs.
Another alternative would be to use the ".bind()" facility that some browsers and frameworks support:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init.bind(Animation); }, false);
}
The net effect is almost exactly the same: that call to ".bind()" returns a function that invokes the function on which it was called (that being the "init" function in the "Animation" object), and does so with its first argument as the this reference (the "context" object). That's the same thing that we get from the first example, or effectively the same anyway.
Here's another nice approach, I think.
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
};
var Animation = {
init: function(){
Animation.doTheMove(); // This will work, but your IDE may complain...
},
doTheMove: function(){
alert('Animation!');
}
};
You might want to use the portotype base approach:
// generate a prototype object which can be instantiated
var Animation = function() { this.doTheMove(); }
Animation.prototype.doTheMove = function() {
// if the object has only one method, the whole code could be moved to
// var Animation = function() {...} above
alert('Animation!');
}
Animation.prototype.otherMethod = function(param1, param2) {
// ...
}
// run the code onload
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', new Animation(), false);
}
Six and a half years later, but I'm hoping my answer can also provide some insight for current and future developers.
I tend to code using literal objects inside of self defined functions, and the original question posted works just fine if another self-executing function is added along with a try and catch statement.
It's very important to point out that it's all about scope and context.
Please correct any drawbacks or provide more effective suggestions of using this method.
(function() {
console.log(this); // window object
var animation = {
init: function() {
this.doTheMove();
},
doTheMove: function() {
alert("Animation");
console.log(animation); // animation object
}
};
(function() {
try {
console.log("animation.init"); // animation.init function
animation.init();
} catch(e) {
console.log("Error is: " + e);
}
})();
})();
This is a snippet from a prototype class i am putting together. The scoping workaround feels a little hacky to me, can it be improved or done differently?
var myClass = Class.create({
initialize: function() {
$('formid').getElements().each(function(el){
$(el).observe("blur", function(){
this.validateEl(el);
}.bind(this,el));
},this);
},
validateEl: function(el){
// validate $(el) in here...
}
});
Also, it seems to me that i could be doing something like this for the event observers:
$('formid').getElements().invoke("observe","blur" ...
Not sure how i would pass the element references in though?
You can indeed simplify that a fair bit:
var myClass = Class.create({
initialize: function() {
var self = this;
// To demo that we keep the instance reference, set a
// property on the instance
this.message = "Hi there";
$('formid').getElements().invoke("observe", "blur", blurHandler);
function blurHandler() {
// `self` references our instance
// `this` references the element we're observing
self.validateEl(this);
}
},
validateEl: function(el){
// validate $(el) in here...
// note that because of the way we called it, within this function,
// `this` references the instance, so for instance:
this.foo(); // alerts "Hi there!"
},
foo: function() {
alert(this.message);
}
});
That uses invoke (as you suggested) and a named function for the handler (doesn't have to be named, but I find that it's very helpful to have your functions have names). The handler is a closure. In the initialize function, I use a variable to point to this because the variable will then be available to the closure. (I called it self because that's a standard practice when aliasing this for this reason.) The handler makes use of Prototype's native functionality of setting this within an event handler to the element being observed. When we call validateEl via the closure's self reference, we're calling it as a member function as per normal, so within validateEl, this refers to the instance.
Speaking of named functions, your initialize and validateEl functions are both anonymous, which means on call stacks and such in debuggers, you'll just see "(?)" rather than a nice, handy name. I always recommend actual named functions; more here.
I can't see nothing wrong with your code :)
About observers you can to something like this:
$('formid').getElements().invoke("observe","blur", function(e) {
this.validateEl(e.element());
}.bind(this));
I think that it will look slightly less verbose if you create a registerBlur method:
var myClass = Class.create({
initialize: function() {
$('formid').getElements().each(this.registerBlur, this);
},
registerBlur: function(el) {
Event.observe(el, 'blur', this.validateEl.bind(this, el));
},
validateEl: function(el){
// validate $(el) in here...
}
});
But I agree with Rui Carneiro, I don't see anything wrong with your code either.