call vs passing object in javascript - javascript

ive been using call a bit recently in some tutorials I've been following but am more used to passing the object. I was wondering is it best to use call. What's the most efficient way?
var editor = function(someParam){
//do something with this
}
var testFunction function(someParam){
editor.call(this, someParam);
}
or
var editor = function(obj, someParam){
var self = obj;
}
var testFunction function(someParam){
editor(this, someParam);
}

I would stick with passing the object to the function as long as you can structure the function that way.
I see call more like a utility if the function cannot be changed anymore and you really have to inject a different this context.
Also, consider using the prototype approach if your function is bound very close to some object that you want to call the function on.
Also, call will be less performant than passing the object to the function, as the injection of a different this has some overhead.

In some cases call is very useful, for example when adding event handler:
window.addEventListener('load', function(){
var cb = document.getElementById('myCheckBox');
cb.addEventListener('change', onchange);
onchange.call(cb); // sets `cb` as `this` in `onchange` function
});
function onchange(){
// I'm using 'this' for current element
if(this.checked)
alert('checked');
}
In this case onchange will be called on window load and every time checkbox checked state changes

Related

non literal reference to self

I have a planet 'class' in javascript. When the user clicks on a html5 canvas the appropriate planet's manageClick function is called which should change the contents of a certain div. The content should change to a button. If the user clicks it then the planet's function createShip should be called.
Now the issue is how do I pass in a reference to the planet itself ? I tried to make a new function that calls createShip() on any object and then call it with the argument 'this'. But it doesn't seem to work. What would be the proper way to do this ?
Planet.prototype.createShip =function() {...}
Planet.prototype.manageClick=function() {
$(".dropdown").html("<button onclick=\"buyship(this)\">Buy a ship</button>");
}
function buyship(obj) {
obj.createShip();
}
Don't use onclick. Instead use jquery to create the button and bind a function to the click event. Using the .bind method of a function you can bind the this value to the function explicitly rather than passing it as a parameter.
Planet.prototype.createShip =function() {...}
Planet.prototype.ManageClick=function() {
var self = this;
var buyShip = $("<button id='buyShip'>Buy a ship</button>").click(buyship.bind(self))
$(".dropdown").append(buyShip);
}
function buyship() {
this.createShip();
}
Fiddle: https://jsfiddle.net/brko5jr7/1/
I hate using this because if you connect any of your object's methods to things like jQuery event handlers, this can become a real pain in the rear if you don't code things just right.
I don't use the prototype approach for coding classes as you did, but prefer to write them as a function declaration.
var Planet = function(){
var ref = this;
ref.CreateShip = function () {
ref.xyz = 123456;
};
ref.ManageClick = function(){
ref.someproperty = "something";
}
}
You'll notice that right off the bat, I declare variable ref which points to the object in scope, and I then use ref inside of members instead of this. It makes for better readability, in my opinion. YMMV.
Instantiation is still the same:
var instance = new Planet();

How can you pass anonymous functions as parameters to existing functions to use later in javascript?

I am trying to create a basic javascript framework that you can pass different things into, including functions for it to execute later. Right now, I'm in a more simple testing phase, but I can't quite get the function calling to work. A piece of my code is here:
[My JS Fiddle][1]http://jsfiddle.net/mp243wm6/
My code has an object that holds different data, and I want to call the method later, but with data that is available at the time of creation. Here is a code snippet of the function that uses the function that is passed to the object:
clickMe : function() {
this.obj.click(function() {
this.func();
});
}
Any suggestions or things I should read are welcome.
The problem is that there're two different contexts:
clickMe : function() {
// here is one
this.obj.click(function() {
// here is another
this.func();
});
}
You can simple pass the function as parameter, like the following:
clickMe : function() {
this.obj.click($.proxy(this.func, this));
}
http://jsfiddle.net/mp243wm6/2/
The problem:
Considering your code in the JSFiddle, you have:
onClick : function() {
this.obj.click(function() {
this.func();
});
},
As noted, you have different contexts going on here.
Consider the snippet this.obj.click(function() { this.func(); }). The first this here is a reference to the framework.events object. The second this here is a reference to whatever will be this when this function get called. In the case of your JSFiddle, when this.func gets called, this is actually the DOM object that represents the <div id="test">TEST</div> node. Since it doesn't have a func function, calling func() on it causes:
Uncaught TypeError: undefined is not a function
You have to understand the following: you have to pass the correct this in which you want the function func to be called.
The solution:
A couple of ways to make it work as you would like:
1. with bind
this.obj.click(this.func.bind(this));
This way, you are telling: "call my this.func function, but make sure that it will be called using the this that I am passing as a parameter". Vanilla JS, no $.proxy stuff.
JSFiddle
2. with a copy of the reference to the actual function
onClick : function() {
var theFunctionReference = this.func;
this.obj.click(function() {
theFunctionReference();
});
},
This way, you will not rely on the value of this outside of the context of the framework.events object.
JSFiddle
The issue is that this is not bound to the correct object. I would suggest you look into Function.bind() because that creates a function with this pointing to the right thing.

javascript passing event in closure

I was trying the following:
f.addEventListener('submit',(function(frm){
var func = (function(e){somefunction(e,frm);})(e);
})(f),false);
But this is failing. I want to pass the form (f) as a static reference and the dynamic event object to the named function 'somefunction'.
What I have above isnt working, what is the right syntax for passing both?
The issue is that each of the functions is being called right away, with undefined actually being passed to addEventListener().
You'll want to instead return one of the functions without its calling parenthesis so the event can call it later:
f.addEventListener('submit', (function (frm) {
return function (e) {
someFunction(e, frm);
};
})(f), false);
Though, with event bindings, you may not necessarily need the closure, as the <form> will be the context (this) of the function passed:
f.addEventListener('submit', someFunction, false);
function someFunction(e) {
var frm = this;
// ...
}
not saure exactly what you are trying to do but, to looks like you are trying to manually pass in the form via the event handler. Instead save a reference and just refer to it in the handler such as
f.addEventListener('submit',function(){
var func = function(e){
somefunction(e,f);
};
},false);
you shouldn't need the self executing functions unless I am missing your intent here

Javascript callbacks losing 'this'

I have an issuer where I lose the this inside this object. The output of the following piece of JavaScript gives me "some-id" and then undefined. When I use this inside a callback function, the scope goes out of the object and it cannot use this any more. How can I get the callback to use 'this' or at least have access to the object?
Since I will make multiple objects, I won't be able to create a 'static' like storage.
Here is my test code that you can use to reproduce my problem. What I would like to have is CheckBox.doSomething() to return the value of this.id which should match some-id for this test case.
function CheckBox(input_id) {
this.id = input_id;
this.doSomething();
$('#some-element').click(this.doSomething);
}
Checkbox.prototype.doSomething = function() {
alert(this.input_id);
}
var some_box = new CheckBox('some-id');
some_box.doSomething();
$('#some-element').click();
I can't even get this to work as I want it to:
function CheckBox2(input_id) {
this.id = input_id;
alert(this.id);
}
CheckBox2.prototype.doSomething = function() {
alert(this.input_id);
}
var some_box = new CheckBox2('some-id');
some_box.doSomething();
Your problem is with this line: $('#some-element').click(this.doSomething);
Why this is a problem
JavaScript methods don't know anything about the object that should be assigned to this, it's set when the method is called either explicitly (with myFunction.call(obj)) or implicitly (when called using obj.myFunction()).
For example:
var x = {
logThis: function () {
console.log(this);
}
};
x.logThis(); // logs x
x.logThis.call(y); // logs y
var func = x.logThis;
func(); // logs window: the fallback for when no value is given for `this`
In your case, you're passing this.doSomething to jQuery, which is then explicitly calling it with the element that was clicked as the value of this. What's happening is (a slightly more complex version of) this:
var callback = this.doSomething;
callback.call(anElement, anEvent);
The solution
You need to make sure that doSomething is called with the right value of this. You can do that by wrapping it in another function:
var cb = this;
$('#some-element').click(function() {
return cb.doSomething();
});
jQuery provides a proxy function lets you do this more simply:
$('#some-element').click(jQuery.proxy(this.doSomething, this));
function CheckBox(input_id) {
this.id = input_id;
this.doSomething = $.proxy( this.doSomething, this );
$('#some-element').click(this.doSomething);
}
The "javascript equivalent" of this is Function#bind but that is not available in every browser and since it seems you are using jQuery I am using the jQuery equivalent $.proxy
Others have already explained the causes of the problem and how to fix it with jQuery. What's left is how you fix it with standard JavaScript. Instead of ...
$('#some-element').click(this.doSomething);
... you write:
document.getElementById('some-element').addEventListener('click', this.doSomething.bind(this));
This changes the context of this inside doSomething. You can also do that with anonymous functions - instead of ...
$('#some-element').click(function(event) {
console.log(this);
});
... you write:
document.getElementById('#some-element').addEventListener('click', (function(event) {
console.log(this);
}).bind(this));
That has been very useful to me in projects with lots of callbacks, e.g. in Node.js (where you don't have to care about outdated browsers).
Edit: getElementById() and addEventListener() instead of $(...).click(...).

javascript anonymous function parameter passing

I have some javascript code (within an object) :
toggle: function() {
var me = this;
var handler = function() { me.progress() };
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
I'm kind of new to javascript, so doing the above as far as I can tell actually passes the me variable into anonymous the function. I was wanting to see if there is a more declarative way to do so? I wanted something along the line of:
var handler = (function(o) { o.progress();})(this));
but that doesn't seem to be working... Am I missing something? Is this a case where "this is the way the language works so just declare a local variable and deal with it"?
UPDATE:
The source to my problem was/is my unclear understanding of scope and closures in javascript. I found this article to help me understand a little more.
You can use ".bind()":
var handler = function() { this.progress(); }.bind(this);
New browsers have "bind()", and the Mozilla docs have a solid implementation you can use to patch older browsers.
The reason
var handler = (function(o) { o.progress();})(this));
doesn't work because it just immediately calls the anon function, therefore immediately calling o.progress() and assigns the return value of the anon function (undefined) to handler. You need to return an actual function from the outer function:
handler = (function(me){
return function(){
return me.progress();
}
}(this));
On the flip side this is equivalent and just as bad looking as bad looking as the variable assignment (but can still be useful, particularly if this needs to be done in a loop, with the changing i rather than the fixed this).
BTW, if the progress function doesn't have any calls to this inside it , just doing handler = this.progress (without the parens) might suffice.
The anonymous function has access to me because it is declared inside of the outer function (the toggle function); it is closed over by the outer function.
Your handler function will be called by setInterval, which passes exactly zero arguments. This means you can't use parameters in the handler function itself.
I you really want to pass me explicitly, you could write a function accepting an parameter, and have that function return an anonymous function without parameters, but which could access the creator function's parameter:
toggle: function() {
var me = this;
var handler = (function (o) { return function() { o.progress() }; })(me);
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
But this basically adds a layer of redirection without really making it more legible. Unless you pull that creating function outside:
function createProgressHandler(o) {
return function() {
o.progress();
};
}
// ...
toggle: function() {
var me = this;
var handler = createProgressHandler(me);
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
What you have there is a closure. The function that is created and assigned to handler keeps a reference to the me object. This is normal, everyday JavaScript, and that's the way that closures work generally.
Have you tried to return the function like this?
var handler = function(o){
return function(){
o.progress();
}
}(me);
Now you can call:
handler();

Categories

Resources