Javascript "this" pointer in Method Invocation Pattern not pointing to object - javascript

I am attempting to use the method invocation pattern in Javascript. I declare a function as an object member.
According to Javascript: The Good Parts, this should result in the this pointer referencing the enclosing object. When I've tried this before, this has been the case.
In the sample of code below, the this pointer reference by the single console.log statement is pointing to the function, not the object. I've double-checked my code and I honestly don't know what's going on.
I could use another pair of eyes on this. Is there something really obvious that I'm missing here, or am I expecting the wrong behavior? Thank you.
EDIT: I had a mistake in my code that I posted (it's been in flux); the keyword inside the anonymous function should be that, not this. Fixed.
DOUBLE EDIT: I've added the rest of my code within the module. I'm trying to write a commonJS module (in accordance with the gameJS library that I'm using) and although I'm not sure where that would be the problem, I'm wondering if it is. Does this change anything?
var gamejs = require('gamejs');
var system = require('app/system');
var input = {
eval_keys: function () {
console.log(this); // This should be the outer object, but shows the function!
var that = this;
gamejs.event.get().forEach(function (event) {
if (event.type === gamejs.event.KEY_DOWN) {
for (var key in that.keyconfig) {
if (that.keyconfig.hasOwnProperty(key)) {
if (event.key === gamejs.event[key]) {
that.keyconfig.key = true;
}
}
}
system.log("KEYDOWN", event.key);
}
if (event.type === gamejs.event.KEY_UP) {
for (var key in that.keyconfig) {
if (that.keyconfig.hasOwnProperty(key)) {
if (event.key === gamejs.event[key]) {
that.keyconfig.key = false;
}
}
}
system.log("KEYUP", event.key);
}
return keyconfig;
});
},
eval_mouse: function () {
/* in progress
else if (event.type === gamejs.event.MOUSE_MOTION) {
// if mouse is over display surface
if (display.rect.collidePoint(event.pos)) {
system.log("mousemove", testcoords);
testcoords = event.pos;
}
}
*/
},
keyconfig: {
K_UP: false,
K_LEFT: false,
K_RIGHT: false,
K_DOWN: false
}
};
exports.eval_keys = input.eval_keys;
Output from Chrome's dev console:
Object {eval_keys: function}
eval_keys: function () {
arguments: null
caller: null
length: 0
name: ""
prototype: Object
__proto__: function Empty() {}
<function scope>
__proto__: Object

It looks like it does work to me, by calling input.eval_keys() after the object is declared.
Also the output you show in the console looks to me to be the output you want -- i.e., the outer object Object {eval_keys: function} containing the method.
The problem seems more to be that you should be seeing the other methods you declared in there, like this:
Object {eval_keys: function, eval_mouse: function, keyconfig: Object}
eval_keys: function () {
eval_mouse: function () {
keyconfig: Object
__proto__: Object
So, as far as I can tell, your question should be "why aren't these other methods showing up within my object in the console?" But I don't what else you are doing with the code that might account for that nor how and when you're invoking the relevant method.
Hope this helps.

It would work if you didn't use an anonymous function with that forEach loop, the this keyword has a different value inside there. You can pass it in a the second parameter:
gamejs.event.get().forEach(function (event) {
// this now refers to the outer this
}, this);
Alternatively you can use your that variable which refers to the outer this value as well.

Well, I figured out what was causing the this pointer to only show an object with one function - it was this line:
exports.eval_keys = input.eval_keys;
On a whim, I decided to add exports.keyconfig = input.keyconfig and it appeared in the console as part of the object.
It looks like the exports keyword does something with the this pointer when it's referenced, even if the this pointer is inside the module in question. I'm not exactly sure how that worked, but it did.
I'm running into more problems, but at the moment, the immediate issue has been resolved. I'll have to do some more reading on this.

Related

call a js function based on the argument passed

I'm trying to call a js function within another one, but use the argument to specify the function. ie depending on the argument passed, it will call a different function
function toggle(n){
if (sessionStorage['toggle'+n]== 0){
check+n();
}
else
}
So, for example, if the argument 'Balloons' was passed as n, then it will call the function checkBalloons(). "check+n();" is not currently working here. Sorry for my lack of simple js syntax!
If the function is defined in the global scope (browser) you can do:
window["check"+n]();
or some tenants like Node.js you would access it from global object.
global["check"+n]();
if it is a part of some other object then you would do the same.
obj["check"+n]();
Functions and properties defined on an object can be accessed using [] convention as well. i.e obj["propFuncName"] will give you reference to it, so in case of methods you add () to invoke it.
If the function is global, you would do this:
window["check" + n]();
or, you could put your function in an object like so:
myNamespace = {};
myNamespace.checkSomething = function(){ /* ... */ }
// call it like this:
myNamespace["check" + n]();
The answers thus far are correct, but lack explanation.
In JavaScript, you cannot call a function by name when that name is a string. What you can do is retrieve a value from an object by name, and if that value happens to be a function, you can then call it. For example:
var myObject = {};
myObject.myFunction = function() { alert('test!'); };
// Get the property on `myObject` called `myFunction`
var theFunctionLookup = myObject['myFunction'];
// Since that property was a function, you can call it!
theFunctionLookup();
In the browser, functions that are defined in the global scope are attached to the window object. For example, this works:
function myFunction() { alert('test'); }
var theFunctionLookup = window['myFunction'];
theFunctionLookup();
You can shorten the last two lines into one:
function myFunction() { alert('test'); }
// Look up and call the function in one line.
window['myFunction']();
For the same reasons, you can use a dynamically-calculated string to look up functions.
function checkBalloon() {
alert('checking balloon');
}
function toggle(n){
if (sessionStorage['toggle'+n]== 0){
window['check' + n]();
check+n();
}
}
toggle('Balloon');
if you do this way:
if (sessionStorage['toggle'+n]== 0){
window["check" + n]();
}
will work

Adding methods to objects

After coding in JS for a while I decided to make my own framework. Something similar to jQuery. But a very very stripped down version. After some googling I put together this code:
function $elect(id) {
if (window === this) {
return new $elect(id);
}
this.elm = document.getElementById(id);
}
$elect.prototype = {
hide: function () { this.elm.style.display = 'none'; },
show: function () { this.elm.style.display = ''; },
toggle: function ()
{
if (this.elm.style.display !== 'none') {
this.elm.style.display = 'none';
} else {
this.elm.style.display = '';
}
}
};
So far this seems to work. But I'm not interested in the functionality. I want to understand the logic. Adding methods part is understandable. Though I didn't understand the function of
if (window === this) {
return new $elect(id);
}
If I remove it, function breaks. Since this is an if statement there are 2 results. True or false. So I tried to remove the if statement and just use return new $elect(id); assuming window === this returns true, but that didn't work. Then I thought it might return false, so removed the whole if statement. That also didn't work. Can someone enlighten me? Also is this code valid? I'm probably missing some stuff.
Just tested on jsfiddle and it doesn't work. Though it works on jsbin o.O
jsfiddle jsbin
EDIT: using $elect(id).toggle(); to call it. Though you can check the demos.
I want to understand the logic.
$elect is a constructor function that is supposed to be called with the new keyword (new $select). If it is not ($elect()), what will happen then? There is no instance constructed, and the this keyword will point to the global object (window) - which we do not want. So this snippet is a guard against that occasion, when it detects that it invokes itself correctly with new and returns that.
If I remove it, function breaks
When you are calling it like $elect(id) without the guard, it will add a elm property to the global object (a global variable in essence) and return nothing. Trying to call the .toggle() method on that will yield the exception undefined has no method 'toggle'.
I tried to remove the if statement, assuming window === this returns true
Well, then you just created an infinite recursive function that will lead to a stack overflow exception when called.
Btw, the proper way to guard against new-less invocation is not to compare against window (the global object might have a different name in non-browser environments) and assume everything else to be OK, but to check for the correct inheritance. You want the constructor only to be applied on instances of your "class", i.e. objects that inherit from $elect.prototype. This is guaranteed when called with new for example. To do that check, you will use the instanceof operator:
function $elect(id) {
if (! (this instanceof $elect)) {
return new $elect(id);
}
this.elm = document.getElementById(id);
}
This also makes the intention of the guard explicit.
To understand how that condition works, you must first know that in the global scope this refers to the window object. In local scope, such as within an object, this refers to the object itself.
Your $elect function is a constructor for your framework. If you call it directly, like so:
$elect('some-id-blah')
It will first realize that you are trying to create an instance (because the condition window === this will evaluate to true), and it will recursively create a new instance of itself. Once it does that, this will no longer refer to the window object, it will refer to the new instance of your library, thus the condition will not be met.
I hope that's somewhat understandable.
Your function is a constructor. By default the this in any function not called explicitly ($elect(), not foo.$elect()) on any other object as a method, will be passed the global object (the window) in this. However the if checks that if the this indeed refers to the window object, the return new $elect(id); is executed. The new $elect(id) does the following:
make a new instance of the $elect.prototype
put the newly created instance in the this and call the method again.
On the second pass, the this === window evaluates to false.
First thing to understand is that this function is calling itself:
function $elect(id) {
if (window === this) {
return new $elect(id);
}
this.elm = document.getElementById(id);
}
First time you call the function window is going to be === this. Because there you're invoking the function as a method. When invoked as a method, functions will be bound to the object the function/method is a part of. In this example, this will be bound to the window, the global scope.
But then with the new keyword, you're invoking the function as a constructor. When invoked as a constructor, a new Object will be created, and this will be bound to that object, so this no longer refers to window the second time around.
With the new instance of $elect, you're then also setting a local variable elm correctly to the instance, allowing your other functions to call on that elm later.
This logic:
if (window === this) {
return new $elect(id);
}
Ensures that, if your constructor function is called as a function:
var foo = $elect(id);
rather than as a constructor:
var fo = new $elect(id);
it will return the correct result - a new $elect object. When called as a function, the default context will be the global context or window when running in the browser, triggering the if clause and returning the result of calling it as a constructor.
Why it isn't working in your fiddle
In the linked fiddle, it is setup to wrap your code in a onload handler. The result of which looks like this:
window.onload=function(){
function $elect(id) {
if (window === this) {
return new $elect(id);
}
this.elm = document.getElementById(id);
}
$elect.prototype = {
hide: function () { this.elm.style.display = 'none'; },
show: function () { this.elm.style.display = ''; },
toggle: function ()
{
if (this.elm.style.display !== 'none') {
this.elm.style.display = 'none';
} else {
this.elm.style.display = '';
}
}
};
}
Because of that, your $elect variable isn't visible outside of the onload handlers scope.
Generally, you would want to add one variable to the global scope to enable access to your framework. Something like this added at the end of your code above will make it visible:
window.$elect = $elect;
Demo Fiddle
If you call it like this:
$elect("theId").hide();
The first time in it is called as a function and will find window === this and do this:
return new $elect("theId")
which creates a new one and call the function again. (Whatever the 2nd invocation of the function returns will get returned.)
The second time in, it is called as a constructor, so window !== this and it will pass down to find the element and store it internally.
Constructor calls that don't return anything from the function itself will return the instance created.
That way the .hide() part will get executed with the proper value of this (the instance created the first time in). And the element will disappear from the screen.
Note that if you call it like this:
var bob = {};
bob.$select("theId").hide();
It will not work because the this is set to bob which gets and 'elm' property set and there is no 'hide' property to be called because its not a $elect kind of object.
NOTE
If all the other methods in the other functions return this you will be able to chain them:
$elect('theId').show().red();
assuming you added a function to color the element red.

Access to public and private methods from event handler closure

I have a difficulty in understanding, how my current JavaScript code works. I've managed to solve a problem in accessing private object method from event handler closure, but I'd like to know why does it work so.
The code utilizes the well-known module/plugin metaphor:
(function(module, $, undefined)
{
function myPrivateCode(e){ /*...*/ }
module.myPublicCode = function(e) { /*...*/ }
module.init = function()
{
var that = this;
$('.clickable').click(function(e)
{
if($(e.target).hasClass('classX'))
{
that.myPublicCode(e.target); // requires 'that' to work
}
else
{
// that.
myPrivateCode(e.target); // will fail if 'that' uncommented
}
});
}
}(window.module = window.module || {}, jQuery ));
In the code I set a click handler which invokes either public or private method. It's perfectly conceivable that we need to pass an object reference into the event handler closure, which is done by that local variable. What is strange to me is that myPrivateCode does neither require that as a refernce, nor fails due to its "privacy". This makes me think that myPrivateCode accesses not the appropriate object, and works somehow differently to expected way. Could someone explain what happens? Certainly I'm missing something.
Both that and myPrivateCode are available to your event handler through a closure. In short, what's going on is that every variable and function you declare inside of another function has access to the outer scope.
myPublicCode, on the other hand, is not available through closures, because it's being assigned to your module object specifically. So the only way to call it is by using module.myPublicCode() (or that.myPublicCode() as you did – but you don't actually need that there, since module is also available).
Your call to myPrivateCode(e.target); is running in the context of the anonymous function that you pass as a handler to the click function.
For more information, read up on closures.
For a simpler example, try out this code:
var foo = function () {
var a = 1;
return function (b) {
return a+b;
}
};
var bar = foo();
bar(1); // 2
bar(1) will always always gives 2, because a = 1 was in scope when the function was created. In your case, a is your that and your handler is the closed function.
http://jsfiddle.net/Fh8d3/

Node.js modules: correct way to refer to sibling functions

This is my current code:
var PermissionsChecker = {};
PermissionsChecker.check = function(id) {
PermissionsChecker.getPermissions(id);
}
PermissionsChecker.getPermissions = function(id) {
// do stuff
}
Two questions:
Is this the right way to construct node.js functions?
Is that line in .check the correct way to refer to a sibling function?
Thanks!
It's perfectly fine. Some notes:
Sibling function isn't really any standard term for methods of the same object. Minor note, but could cause confusion.
When a function is called as a method on some object, then the value of this inside that function refers to the object on which it was called. That is, calling check like this:
PermissionsChecker.check()
...allows you to write the function like this:
PermissionsChecker.check = function(id) {
this.getPermissions(id);
}
...which is more succinct and probably more common.
Nothing about your question is specific to node.js. This applies to JavaScript in the browser (or anywhere else), too.
You could save some typing by rewriting your example like this:
var PermissionsChecker = {
check: function(id) {
this.getPermissions(id);
},
getPermissions: function(id) {
// do stuff
}
};
So long as the function is called with PermissionsChecker.check(), you can refer to the object with this.
CodePad.
What you've done above is called an object literal, but you could choose the prototypal way also (when you need to instantiate objects - OOP stuff).
You can call this inside to refer to another object property:
PermissionsChecker.check = function(id) {
this.getPermissions(id);
}

Associative Array / Object can't be read in functions

At the very beginning of the javascript file, I have:
var lbp = {};
lbp.defaults = {
minLength: 40
};
I can successfully alert it afterwards, with:
alert(lbp.defaults.minLength);
But as soon as I put it inside a function, when I alert, I get "Undefined". What gives, and how do I avoid this? Is it absolutely necessary to pass this variable into each function, for example, by doing:
function(lbp) { alert(lbp.defaults.minLength); }
I would have thought that defining it first, it would attain global scope and not be required to be passed in?
Thanks in advance for enlightening me :)
====================================
EDIT:
The problem seems like it might be my initialize function is itself defined within lbp. Is there any way to use this function var, and still use lbp vars inside it?
lbp.initialize = function() {
alert(lbp.defaults.minLength);
};
The full bit of code looks like this:
<script type="text/javascript">
var lbp = {
defaults: {
minLength: 40
}
};
lbp.initialize = function() {
alert(lbp.defaults.minLength);
};
window.onload = lbp.initialize;
</script>
Are you actually passing lbp as the argument? Otherwise the parameter with the same name will hide the global variable.
Use this.
var lbp = {
defaults: {
minLength: 40
}
};
lbp.initialize = function() {
alert(this.defaults.minLength);
};
window.onload = function() { lbp.initialize(); };
If you call initialize as a method of lbp, this will point to lbp. When you assign a function to an event handler, such as window.onload, you are essentially copying the body of that function to the object on which the event handler is defined. So,
window.onload = lbp.initialize
is the same as
window.onload = function() {
alert(this.defaults.minLength);
};
Now, this is pointing to window, which is obviously not what we want. By wrapping the call to lbp.initialize() in a function, we preserve the context of this within that function and we can make sure that it always points to lbp. Check out this for a more complete explanation.
This works for me from javascript console in Firefox:
javascript:var lbp={}; lbp.defaults={minLength: 40};function xx() { alert(lbp);alert(lbp.defaults);alert(lbp.defaults.minLength); }; xx();
Gives output [object Object], [object Object], 40.
So, it seems there might be some problem with some associated code, which is not shown?
In the original code where you are trying to use lbp in a function. You are passing lbp in as an argument. This would hide the lbp from the global scope with a local (to the function) variable of the same name (unless when calling the function you passed lbp in again).
//this is what you have and will not alert a thing other
//and will probably throw an error
function(lbp) { alert(lbp.defaults.minLength; }
//should just be this with no argument. this will allow
//the function to see lbp in the global scope.
function() { alert(lbp.defaults.minLength; }
by passing lbp as a parameter in the first function it is not seen inside the function as the global object, but the local argument.

Categories

Resources