OO JavaScript: Accessing another method of the same object? - javascript

I have a JavaScript object called ShippingUI:
function ShippingUI(){
...
}
It has several methods:
ShippingUI.prototype.UpdateItemQTYs = function(ItemJQOBJ, NewQTY)
{
...
}
ShippingUI.prototype.EH_SortableRecieve = function(event, ui)
{
...
}
The "EH_SortableRecieve()" function is a drop event handler. When it runs, it needs to call "UpdateItemQTYs()", a sister function in the same object. I'm trying to use:
ShippingUI.prototype.EH_SortableRecieve = function(event, ui)
{
this.UpdateItemQTYs('ABCD',123);
}
But keep getting the error:
"this.UpdateItemQTYs is not a function"
I'm guessing that "this" is pointing to something else... so how do I get the 'real' "this"?
Event Binding method:
// Takes a Jquery Object and makes it sortable with our custom options
this.MakeSortable = function(JQOBJ)
{
JQOBJ.sortable({
connectWith: '.connectedSortable',
items: '.ItemLineWrapper',
dropOnEmpty: true,
axis: 'y',
receive: this.EH_SortableRecieve
});
}

There is something missing in your examples which is how EH_SortableRecieve is called. But based on what you say it should be used as I'm thinking it is used something like this:
htmlelement.onmouseup = shippingObject.EH_SortableRecieve;
In which case you should be aware of Javascript's binding of this in methods. Specifacally, in event handlers this is bound to the window object instead of the object the method belongs to. This is a general feature of javascript: methods can be re-bound at runtime. In other words, objects can steal other object's methods. For example, I can have my object slebetmans_object steal your method and re-bind its this with the following:
shippingObject.EH_SortableRecieve.apply(slebetmans_object,parameters);
There are several strategies to get around this. You can use a closure to capture your object:
htmlelement.onmouseup = function(){ shippingObject.EH_SortableRecieve() };
You can use a closure in the object's constructor to capture the correct reference to your object:
function ShippingUI () {
var self = this; // since self is resolved at the time the object is created
// it always reference to the "correct" object
this.EH_SortableRecieve = function(event, ui)
{
self.UpdateItemQTYs('ABCD',123);
}
}
There are probably other ways to do this but these are the two most common that I personally use.

The problem is that when you register the function as an event handler, its relationship to your object is lost.
How are you registering the event handler? If it's with jQuery, you can use the new "proxy" API:
$('#someButton').click($.proxy(myShippingUI, 'EH_SortableReceive'));
That'll make sure the object (I used "myShippingUI" as a sample instance of your class) is acting as the "this" pointer. There are other ways to do this to, but that's a simple one.
The "$.proxy" thing is new with 1.4.

function ShippingUI(){
...
}
ShippingUI.prototype = {
UpdateItemQTYs : function(ItemJQOBJ, NewQTY)
{
...
},
that = this,
EH_SortableRecieve = function(event, ui)
{
that.UpdateItemQTYs('ABCD',123);
...
}
};
The above should work too... All you need is the reference to ShippingUI object you are currently using in this.

Related

Where does an anonymous function gets its arguments

I'm learning to develop Windows 8 style applications with the help of a book. The chapter I'm reading focuses on HTML, CSS and JavaScript languages for developing. The application displays in a ListView the images you have in the My Pictures Folder and deletes them when the user clicks or taps an image. Here is the code that implements the deletion of an image in the ListView:
var lv = document.getElementById('lv');
lv.addEventListener('iteminvoked', function (eventObj) {
eventObj.detail.itemPromise.then(function (listViewItem) {
var binding = files.dataSource.createListBinding();
binding.fromIndex(listViewItem.index).then(function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
});
});
});
My question is, where does the eventObj parameter of the anonymous function in the addEventListener method gets its value? I have found a similar question asked here: Passing arguments in anonymous functions in JavaScript, but i cannot fully understand it. I searched the documentation for addEventListener on MSDN but it just says it takes an event handler function, but it doesn't say anything about the parameters. Thanks in advance.
It's rather simple: whatever function internally calls that callback passes the arguments. See, addEventListener tells the executing Javascript engine to call the callback function you specify whenever an event occurs. The javascript engine saves your anonymous function in some variable - and cann call it later on using that exact variable, passing any number of arguments.
To illustrate it, consider something like this the internal function that handels events (purlely fictional, just to illustrate how it could be done):
var callbacks = [];
function addEventListener(newEvent, newCallback) {
callbacks.push({event : newEvent, callback : newCallback});
}
function handleEvent (someEvent) {
for (var i = 0 ; i < callbacks.length ; i++ ) {
if (callbacks[i].event == someEvent.name) {
callbacks[i].callback(someEvent);
}
}
}
Some more explanation:
As javascript is a so-called "functional language", functions are just values of variables.
function someFunc () {}
is actually just some kind of shortcut (technically it's not, but it does the same thing) for
var someFunc = function () {}
This having said, it's of cours possible to associate multiple names with one function:
var someFunc = function () {}
var sameFunc = someFunc;
var stillSame = somefunc;
var alsoSame = stillSame;
and you can call that function using any of those names, including passing arguments of course:
var someFunc = function (arg) { alert(arg); }
var sameFunc = someFunc;
sameFunc("It worx");
You can even call a function without ever naming it:
(function () {alert("test")})();<
or
(function (arg) { alert(arg); })("test")
Using this concept to perversion finally leads (long way to go however) to things like the y-combinator.
Event handlers may be attached to various objects including DOM
elements, document, the window object, etc. When an event occurs, an
event object is created and passed sequentially to the event
listeners.
Source: https://developer.mozilla.org/en-US/docs/Web/API/Event
An event listener or event handler can be an anonymous function or named function, it really doesn’t matter. The point is that it’s the event interface that defines the event object that is passed to the handler.
To find out exactly the event property from the event you are using, please refer to the windows docs: http://msdn.microsoft.com/en-us/library/windows/apps/br211827.aspx
The arguments recieved by the event listener are sent from the dispatchEvent, i.e. when the event dispatched it passes an event object to your handler.
Refer to this documentation on how to create and dispatch the event. The event object can vary in structure to convey information to the eventhandler to execute necessary steps. So in your case when you do lv.dispatchEvent(newevent) this sends an newevent as eventObj to your event handler.
Keep in mind there can be multiple eventhandlers listening to an event so the browser maintains a stack for the eventlisteners running them sequentially with each of them passed eventObj.
Anonymous function is no different from a named function. In JavaScript functions are first-class objects meaning regular objects. So you can pass them like regular objects(numbers,strings) without having to name them. Only thing is reuse becomes an issue.
What you need to understand this code is to rewrite it a bit:
var lv = document.getElementById('lv'),
invokeHandler = function (eventObj) {
var promiseFullfilled = function (listViewItem) {
var binding = files.dataSource.createListBinding(),
anotherPromiseFullfilled = function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
};
binding.fromIndex(listViewItem.index).then(anotherPromiseFullfilled);
};
eventObj.detail.itemPromise.then(promiseFullfilled);
};
lv.addEventListener('iteminvoked', invokeHandler);
This code works just the same, however it is now obvious that addEventListener or then actually do not know anything about the callback functions they are passed with. They can, however, use Function.prototype.call or Function.prototype.apply to apply arguments:
// This is PSEUDOCODE, event model actually works in a totally different way
HTMLElement.prototype.addEventListener = function(eventType, callback, bubbles) {
// callbacks is some internal collection for this specific element, probably available via a closure, looks something like:
// {
// 'someEventType': [callback1, callback2],
// 'someOtherEvent': [callback1, callback3, callback4]
// }
callbacks[eventType].push(callback);
}
// This is called whenever an event is triggered on an element
HTMLElement.prototype.dispatchEvent = function(event) {
callbacks[event.type].forEach( function(callback) {
return callback.call(this, event); // the callback is called with 'this' set to the element DOM object, and 'event' is the first argument
});
// then it can bubble or cancel depending on the event type and callback results
}
it is a CustomEvent, and all the process is like that:
//you add a anonymous function to a specific listener
lv.addEventListener('iteminvoked', function (eventObj) {
console.log(eventObj===myEvent);
});
//somewhere in your code a CustomEvent gets created based on "iteminvoked" key
var myEvent = new CustomEvent("iteminvoked", {
itemInfo: {
name: "yourItem"
},
bubbles: true,
cancelable: false
});
//somewhere when an item gets invoked this code raise the `iteminvoked` trigger
lv.dispatchEvent(myEvent);
all the functions that are passed as a listener are stored based on the key, something like:
var observers = {
"iteminvoked" : [f1, f2],
//other keys
}
it doesn't have anything to do with not having name, the function object is stored in the some kind of array. and dispatchEvent goes thru the array and invokes all the functions, and pass the myEvent as their parameter. It is a Observer pattern, implemented in javascript, I have implemented it once in my own javascript library like:
var lv = /*your element*/;
if(observers["iteminvoked"]){
for(var i=0;i<observables["iteminvoked"].length;i++){
var func = observables["iteminvoked"][i];
var o = func.call(lv, myEvent);
//this line is to support return false
if(o!==undefined && o===false) break;
}
}
as you can see it is dispatchEvent resplonsiblity to invoke all the observers, and your function no matter it has name or not gets invoked with the lv as the this context and myEvent as the parameter.

Should I avoid creating this JavaScript closure?

This may be a bit abstract but I'm trying to get my head round JavaScript closures etc. Take the following code:
function MyObj() {
var me = this;
this.foo = function(bar) {
// Do something with 'bar'
}
// Set up lots of local variables etc.
// ....
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
$(document).on('click.myobj', 'input.special', function() {
// Do something with the <input> that triggered the click event
me.foo(this);
});
});
}
var myObj = new MyObj();
The anonymous function passed to that is bound to the click event creates a closure that references me. What I want to know is whether it's better to do something like this instead (to avoid the closure):
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
(function(localMe) {
$(document).on('click.myobj', 'input.special', function() {
// Do something with the <input> that triggered the click event
localMe.foo(this);
});
})(me);
});
Is this a better approach, or am I being overly paranoid about creating a closure? Alternatively, is there a "third way"?
EDIT
Additionally, would it be better to do something like this:
$(window).load(function() {
// Add a delegated click handler to specific <input> elements
$(document).on('click.myobj', 'input.special', {localMe : me}, function(event) {
// Do something with the <input> that triggered the click event
event.data.localMe.foo(this);
});
});
The latter is (AFAIK) more efficient, but probably not measurably so unless used in a tight loop.
The reason is that all variable dereferencing must follow the scope chain. In the latter case, the variable localMe can be found in the anonymous function's parameter list.
In the former case, the variable isn't found there, but in the outer scope. This traversal up the scope chain takes extra time.
Anonymous functions are massively used in javascript now (as arguments and as an immediate function for scopes/closures). There's no performance problem with that.
But you can have a problem of code reading maybe. Because when you see a variable, you must check where the variable is from. But no big deal here.
And in your second example, you still have a closure "break". Because in your anonymous function in the click, you use the localMe variable. And the localMe is an argument of a fonction outside of your fonction.
// Here, 'me' is a direct local variable.
$(window).load(function() {
// Here, we are in an anonymous fonction, so 'me' is not a direct variable anymore. But you still can access it.
// Add a delegated click handler to specific <input> elements
(function(localMe) {
// Here, 'localMe' is a direct local variable.
$(document).on('click.myobj', 'input.special', function() {
// We are in an anonymous function, so 'localMe' is not a direct variable anymore.
// Do something with the <input> that triggered the click event
localMe.foo(this);
});
})(me);
});
If you really want to avoid a closure "break", you should bind your function to your object. But note that not every browser support the bind method on functions.
You will always create a closure if you bind the event from the constructor. In fact, you even need the closure to preserve the reference to your instance. However, you might do something like this:
function MyObj() {
this.foo = function(bar) {
// Do something with 'bar'
}
// Set up lots of local variables etc.
// ....
}
var myObj = new MyObj();
$(function() {
$(document).on('click.myobj', 'input.special', function() {
myObj.foo(this);
});
});
If you do only create a singleton instance of your constructor, it won't matter anyway.
I would probably do it this way:
var bind = function( fn, me ) { return function() { return fn.apply(me, arguments); }; },
Object = (function() {
function Object() {
this.handler = bind(this.handler, this);
// Add a delegated click handler to specific <input> elements.
$(document).on("click.myobj", "input.special", this.handler);
}
Object.prototype.foo = function( bar ) {
// Do something with "bar".
};
Object.prototype.handler = function( event ) {
// Do something with the <input> that triggered the click even.
return this.foo(event.currentTarget);
};
return Object;
})();
var obj = new Object();
This skips the uses of closures and iifes, using .apply instead. Not sure if it is more efficient or not, but it is another option.

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

Firefox add-on - `this` works in one method but fails in another method of the same object

I am developing an add-on for Firefox (3.6.*). in the following code notify called from inside init works fine, but I get an error saying this.notify is not a function when it is called from within onPageLoad. Why is that?
Also when I change the call to myextobj.notify('title', 'msg'), it works. The same is true for accessing variables. So, what is the difference between this and the object name as a prefix?
var myextobj = {
init: function() {
this.notify('init', 'We are inside init');
...
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
},
onPageLoad: function(aEvent) {
this.notify('onPageLoad', 'We are inside onPageLoad');
...
},
notify: function (title, text) {
Components.classes['#mozilla.org/alerts-service;1'].
getService(Components.interfaces.nsIAlertsService).
showAlertNotification(null, title, text, false, '', null);
}
};
window.addEventListener("load", function() { myextobj.init(); }, false);
When you do this:
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
you just add the function that is hold in onPageLoad as event handler. The connection to the object is lost and this will refer to the global object when executed.
Just create an anonymous function as you do for the load event:
var that = this; // capture reference to object
appcontent.addEventListener("DOMContentLoaded", function(event) {
that.onPageLoad(event);
// myextobj.onPageLoad(event); should also work in this case
}, true);
Remember that functions are first class objects in JavaScript, they can be passed around like any other value. Functions have no reference to an object they are defined on, because they don't belong to that object. They are just another kind of data.
To which object this refers to in a function is decided upon execution and depends on the context the function is executed in. If you call obj.func() then the context is obj, but if you assign the function to another variable before like var a = obj.func (that is wat you do with adding the event handler (in a way)) and then call a(), this will refer to the global object (which is window most of the time).
When onPageLoad is called for the event, 'this' would not be referring to your myextobj. Because it wasn't called in the context of your object myextobj.
The way I deal with this is, by having all member functions of an object using the following convention.
var myObj = {
.....
counter: 0,
.....
myFunction: function () {
var t = myObj;
t.myOtherFunc();
},
....
myOtherFunc: function() {
var t = myObj;
t.counter++;
}
};
See how, I'm aliasing myObj as t, to save on typing and making my intent of using this clear.
Now you can call your methods safely from any context without worrying about what this would be referring to. Unless you really want the standard behavior; in that case, you may like to look at the call and apply methods. This link might help: Function.apply and Function.call in JavaScript
You may also want to look at a recent addition to JavaScript (would be available in FireFox 4): the bind method: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Another link, which directly addresses your problem: https://developer.mozilla.org/en/DOM/element.addEventListener#The_value_of_this_within_the_handler
The other way to add an event listener without losing track of this is to pass this itself as the event listener. However you are limited in that the function is always called handleEvent, so it's less useful if you have many listeners (unless they are all for different events, in which case you can switch on the event's type).

Javascript Object Inheritence

I'm creating a control for Google maps v2. While creating my control I've found a design challenge and want to find an appropriate solution. Here's the goods.
A custom Google control inherits from GControl;
myControl.prototype = new GControl();
Next I need to overload the initializer so here it is.
myControl.prototype.initilize = function (map) {
//do some work and return stuff
};
Now within my custom controls initlize function I create a couple elements which, using the GEvent class, I subscribe to various events. To make my callback functions managable, I included them into the controls prototype.
myControl.prototype.onEvent = function(e){
//do some work;
//modify the members of the current myControl instance
};
Within my callback function "onEvent" I want to modify members within my control. What is the best way to access my control from the function? The keyword "this" cannot be used because that is a reference to the element that was clicked, in my case a div. And I can't access the members through the prototype because I need a specific instance of the object. The only viable solution I've considered is to create my control globally in one of my scripts. Is this the best method?
The easiest thing that I can think, it to define your onEvent method within your constructor, there you will have quick access to the current object instance, and you will not have to modify your public API:
function MyControl () {
var instance = this; // store a reference to 'this'
this.onEvent = function (e) {
// use the instance variable
instance.otherMethod();
};
}
Note that in this approach, the onEvent property will exist physically in your object instances (obj.hasOwnProperty('onEvent') = true).
Edit: You can simply use the GEvent.bind function:
GEvent.bind(map, "click", myObj, myObj.onEvent);
The above bind method will enforce the context, so the this keyword inside myObj.onEvent will point to the myObj object instance when the event is triggered, it will work with your code without problems.
I'm not familiar with how you subscribe to events using GEvent, so I'll make that part up. Do this:
myControl.prototype.onEvent = function(e, instance) {
// do some work
// modify members of 'instance'
};
function wrap(handler, instance) {
return function(e) {
handler(e, instance);
}
}
GEvent.Register('Event', wrap(instance.onEvent, instance));

Categories

Resources