non literal reference to self - javascript

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();

Related

copy function to local variable and keep this-pointer

I have a function on a prototype like this:
Car.prototype.drive = function() {
this.currentSpeed = this.speed;
}
I want to call this function very often in another function which is also part of the prototype Car. And because I'm lazy I don't want to rewrite the this all the time. So I would like to copy a reference to the function to a local variable:
Car.prototype.doSomeThing = function() {
var driveReference = this.drive;
driveReference();
}
But when I call driveReference(), the this-pointer of driveReference() points to Window instead to my instance of Car.
Is there any possibility to prevent that?
(apply() would work, but that is even more verbose then using this)
You can use Function.prototype.bind to bind the context of a function to whatever you like:
Car.prototype.doSomeThing = function() {
var driveReference = this.drive.bind(this);
driveReference();
}
You could write
var driveRef = this.drive.bind(this);
but that can have some possibly unwanted performance impact. Or you could just copy this to a shorter variable name:
var me = this;
me.drive();
Explicitly using a reference to the context object is a pretty basic design feature of JavaScript, so it's hard to get around it.

"this" in javascript objects with closures behaving inconsistently

So, this is probably answered somewhere on this site, but I can't find it, if it is.
I'm having trouble figuring out why one of my this references inside functions seems to be resolved when I create the object, and one when I call the function that has the reference inside it. Here's some code:
function MyObj (name) {
this.locked = false;
this.name = name;
this.elem = null;
this.func1 = function () {
if (this.locked) return;
/* code that changes this.name here */
this.elem.innerHTML = this.name;
};
this.func2 = function () {
this.locked = !this.locked;
if (this.locked) this.elem.className = "locked";
else this.elem.className = "unlocked";
};
}
var myObjGlobal = new MyObj("foo");
function callFunc1 () {
myObjGlobal.func1();
}
Then I have a function that is called on document load:
function onLoad() {
var myElem = document.getElementById("myElem");
myObjGlobal.elem = myElem;
myElem.onclick = myObjGlobal.func2;
document.getElementById("myButton").onclick = callFunc1;
}
I've made sure all my html elements have the right ids. When I click myButton, I get no errors. However, when I click myElem, I get Uncaught TypeError: Cannot set property 'className' of undefined.
Why is the first this set when I call the function, and the second this set when I create the object? (Or so it seems?)
here's a working jsfiddle showing the problem (with the given example code).
Thanks in advance!
myElem.onclick = myObjGlobal.func2;
This doesn't do what you think inn JavaScript. It doesn't give you func2 with the object "attached" to it in any way; it just gives you func2. When it gets called later, it's called as a method of myElem, so that's what this is.
This is a gigantic and awful wart in JS. :)
You can either wrap it in another function:
myElem.onclick = function() {
myObjGlobal.func2();
};
Or use .bind, which does effectively the same thing, and which is supported almost universally nowadays:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);
Note also that assigning to onclick is a little rude, since you'll clobber any existing click handler. You may want addEventListener instead.
myElem.onclick = myObjGlobal.func2;
This loses myObjGlobal entirely; myObjGlobal.func2 is just a function, with nothing tying its this to anything. In JavaScript, the this of a function is determined when it’s called, not when it’s defined. This is a fantastic and useful feature of JavaScript that’s much more intuitive than, say, Python. When myElem.onclick is called, it’ll be called with this bound to myElem.
Function.prototype.bind is a utility to do what you’re doing with callFunc1, by the way:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);

call vs passing object in 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

Calling function by value of variable and keeping scope of this pointer

I have been fiddling with code to call a function with the name of the value of a variable and then keep the this scope when called, but the this pointer seems to be in context of the element I have used jQuery's bind method on, rather than the object the function I might be calling is within. To clarify here´s some code to illustrate the problem:
classname.prototype = {
bindElementToFunction: function(element, functionToCall){
$(element).bind("click",
{realThis: this, functionToCall: functionToCall},
this.callFunction);
},
// I had hoped I could change the this pointer back to the object by running
// it through this function, I have tried using .apply and .call but I can't
// seem to get them to work with function pointers
callFunction: function(event){
var realThis = event.data.realThis;
var functionToCall = event.data.functionToCall;
functionToCall = realThis[functionToCall];
// I have tried .apply and .call in several different ways but can't seem
// to get them to work in this context
functionToCall();
},
abitraryFunction: function(){
this.test();
},
};
The problem here is then that everything works fine up until abitraryFunction where this is still referring to the element from the bind function. I have tried doing .apply() with the appropriate this pointers, but they do not seem to work.
So here's the question how do I change the context of the "this" pointer in combination with function pointers?
Feel free to scrap all the code I have written, as long as I am able to do a bind function to an element that then runs a method within a object where "this" is refferring to the object the method is within.
Thanks
I think the jQuery bind is making your code way more complicated than it needs to be. The JavaScript bind() function works perfectly:
http://jsfiddle.net/bQGWS/
By simply assigning a function to the onclick (or any other event hook) of an element, this is evaluated from the element's point of view and so points to the element itself.
When you use bind, you end up with a copy of the function where this is effectively replaced with the var you passed into bind().
classname = function(){}
classname.prototype = {
method: function(){
try {
alert( this.othermethod() );
} catch(e) {
// Method doesn't exist in scope
alert( 'Wrong scope :(');
}
},
othermethod: function(){
return 'hello desired scope!';
},
referenceToElement: function(elementId, functionname){
var el = document.getElementById(elementId);
// Just assigning the function as is
el.onclick = this[functionname];
},
bindToElement: function(elementId, functionname){
var el = document.getElementById(elementId);
// Using the bind function to create a copy in the
// scope of this (within the prototype)
el.onclick = this[functionname].bind(this);
}
}
var instance = new classname();
instance.referenceToElement('reference', 'method');
instance.bindToElement('bound', 'method');

Using jQuery, how do I add elements to a jQuery object that's yet to be created?

Perhaps I am doing this wrong and suggestions on how to improve my code are appreciated. My situation is this: I have a toolbar with different elements that are populated by a callback. I do not want to use the show() or hide() commands, I prefer to use detach, but I think there should be a nice way to deal with it. Here's my code:
entryView = function _entryView() {
var menuButton = $('<div/>').addClass('menuButton');
toolBar();
$.getJSON('ajax', function(o) {
var $enum2 = ss.IEnumerator.getEnumerator(dto.TransmittalDates);
while ($enum2.moveNext()) {
var dateTime = $enum2.get_current();
$('.menu').append($('<div/>').addClass('menuitem').text(dateTime.toString()));
}
});
}
toolBar = function _toolBar() {
var flyoutMenu = $('<div/>').addClass('menu');
$('.menuButton').click(function(o) {
$('.menubutton').append(flyoutMenu);
});
I did a quick cut and paste and renamed the variables to make them make sense. As you can see on the entry I build the toolbar and the very last thing I do is the ajax call. The menu, however, is not created until the "click" event, so appending is not possible.
I realize that having global variables is bad, so I'm trying to avoid that, but I think the best situation would have the ajax call populate a Menu variable and when the DOM is created, to pull from that same Menu item. How do I pull this off? Is there a better way to do it?
Edit: Fubbed a bit on the toolbar function, I think I have it should be correct now.
I'm confused by some parts of your code:
What's the entryView function and when is it called?
Why does the toolBar function exist as opposed to being inline? From where else is it called?
Why are you creating functions like that? Creating a variable without var is bad practice, always makes global variables, and will be forbidden in ES5 strict mode. You should create functions like this:
var someFunction = function(arg1, arg2) { … };
or like this:
function someFunction(arg1, arg2) { … }
Why are you giving each function a second name (e.g. _toolBar)? The "private" name will only be in scope inside the function.
The menu doesn't have to be in global scope, just in a scope that's common to both functions.
I would refactor it like this (knowing very little about the design of your application), inlining toolBar:
function entryView() {
var menuButton = $('<div/>' {'class': 'menuButton'}), menu = $('<div/>', {'class': 'menu'});
$.getJSON('ajax', function(o) {
var $enum2 = ss.IEnumerator.getEnumerator(dto.TransmittalDates);
while ($enum2.moveNext()) {
var dateTime = $enum2.get_current();
$('.menu').append($('<div/>' {'class': 'menuItem'}).text(dateTime.toString()));
}
});
menuButton.click(function(o) {
menuButton.append(menu);
});
}

Categories

Resources