Extending methods in Javascript classes - javascript

I'm ("still", for those who read my previous posts) working on an ICEFaces web application.
This question can be interpreted as general Javascript question, so read on if you don't know much about ICEFaces
I need to extend the behaviour of the classes created by ICEFaces Javascript framework, in particular ToolTipPanelPopup.
I cannot modify the source code of the library (otherwise I would have achieved my goal).
This is how ICEFaces defines the class (much like jQuery and other Javascript frameworks).
ToolTipPanelPopup = Class.create({
[...]
showPopup: function() {
[...]
},
updateCordinate: function(event) {
[...]
},
[...]
});
My question is very simple
How do I extend the behaviour of showPopup() function in order to run my custom function at the end of it?
I mean something like following Java example code that supposes inheritance
public void ShowPopup()
{
super.ShowPopup();
customMethod();
}

Something like this should work:
var original = ToolTipPanel.showPopup;
ToolTipPanel.showPopup = function() {
original(); //this is kind of like the call to super.showPopup()
//your code
};
I tried out this trivial example in Firebug, and it seems to work:
var obj = {
func: function() {
console.log("foo");
}
};
obj.func();
var original = obj.func;
obj.func = function() {
original();
console.log("bar");
};
obj.func();
Firebug output:
foo
foo
bar
So what's happening here is that you're saving a reference to the original showPopup function. Then you're creating a closure and assigning it back to showPopup. The original showPopup is not lost, because you still have a reference to it in original. In the closure, you call the function that original references, and then you have your own code. Just swap around the order if you want to do something first before you call original. Since you're using a closure, original is lexically bound to the current scope and should be available every time the new showPopup is called (if I'm wrong about this, someone please correct me).
Let me know if this works out for you.

Related

Why would an event listener in a JavaScript class see old context variable?

so I'm having a problem that seems to defy everything I know about how scope is handled in JavaScript with anonymous functions - but it could be something else I'm not thinking about.
I have a JavaScript object, called Element, with a constructor similar to this:
function Element(boxElement) {
var self = this;
// Set jquery instance variables
self.pageElement = null;
self.boxElement = boxElement;
... blah blah blah
// Implement triggers to empty functions
self.onElementClicked = function () {};
// Bind listeners
self._bind_listeners();
}
The bind_listeners method is defined as such
Element.prototype._bind_listeners = function() {
var self = this;
self.boxElement.on('click', function (e) {
// Don't handle if handled already
if (e.isDefaultPrevented()) return;
console.log("Got past the return");
self.onElementClicked();
});
};
And there's also a method to set the callback method onElementClicked:
Element.prototype.on_element_click = function(callback) {
var self = this;
self.onElementClicked = callback;
};
The problem I am encountering is that if I set my callback using the on_element_click method, my method doesn't see the current instance - it sees what the instance would look like just after construction.
More specifically to my situation, there's an instance variable called boxElement that refers to a JQuery element - and in Chrome's console I can see that the instance (self) still does refer to the correct element on the page, but the onElementClicked instance variable (and others) do not seem to be set from within the listener.
Feel free to revise my explanation or ask for clarification.
From the implementer perspective:
If I do this:
// Set default listener for element click
formElement.on_element_click(function () {
console.log("Hello");
});
The listener never says Hello because onElementClicked doesn't appear to be set.
However, if I instead do this:
formElement.boxElement.click(function () {
console.log("Hello");
});
It successfully says "Hello" and makes me confused.
I found the solution to my specific problem, which is a good example of how an error like this can occur. (offtopic: please feel free to add answers for other ways to produce this error - it is a very non-intuitive problem and will always be caused by an external factor)
It turns out the class I was testing with is a class that extends my Element class - BUT, it does so improperly / VERY VERY badly!
As embarrassing as it is to post this, here's the original constructor of my "subclass" (quotes for reasons soon apparent):
function StrikeoutFormElement (formElement) {
var self = this;
// Set reference to form element
self.fe = formElement;
$.extend(self, self.fe);
// Override methods
self.on_reposition(function () {
self._on_reposition();
});
}
I used JQuery's object extending function and a hacky workaround to override something. I have learned the hard way to NEVER use JQuery's extend for OOP, as it is only intended for data manipulation rather than as a language tool.
The new constructor looks like this:
function StrikeoutFormElement (elem) {
var self = this;
}
// Extend the FormElement prototype
StrikeoutFormElement.prototype = Object.create(Element.prototype);
StrikeoutFormElement.prototype.constructor = Element;
This is a method described in an MDN article somewhere. I'll post the source when I find it if someone doesn't beat me to it.
Shoutout to anyone who looked at this obscure problem and attempted to figure it out!

scope Issue seeing object methods

I have tried searching through a lot of S.O. pages but nothing has touched EXACTLY on this top while also NOT USING JQUERY.... I am trying to stick to pure JavaScript as I want to learn it 115% before advancing my current knowledge of JQuery.
I have an object called ScreenResizeTool like this...
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg);
}, true);
}
and a method like this...
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
My trouble is probably obvious to an experienced JavaScript user but I am having trouble not making this into a messy dirty awful OOP set. I have done various tests to show and prove to myself that the this inside the addEventHandler changes when it becomes bound to the window. This much I assumed before testing but I was able to see that once window.resize event happens the listen method is gone and not a part of the global window variable....
I have also tried adding a this capture such as this.me = this inside the object constructor however it also couldn't see the me variable once it ran. Once the window took the function over it no longer knew anything about the me variable or any reference to my class methods....
I am aware that I could separate this differently but my goal here is to learn how to fully encapsulate and use as many clean OOP structures as possible as I just came from the .NET world and I need it in my life.
I am also aware that I could make messy calls and or store this object or access to the methods inside the window variable but that seems outright wrong to me. I should be able to fully encapsulate this object and have its events and methods all implemented in this class structure.
I also know that the currImg variable is not going to be seen either but lets start small here. I assume once I figure out my incorrect train of thought on scope for JavaScript I should be fine to figure out the currImg problem.
I know there's 1000 JavaScript programmers out there waiting to rip me a new one over asking this simple question but I gotta know...
Thoughts anyone?
this inside a function bound to a DOM Object (like window) will always refer to that object.
this inside a constructor function will always refer to the prototype.
A common practice to circumvent the this issue, as you mentioned, is to cache it in a variable, often called self. Now you want the variables and properties of your object available after instantiation, so what you need is the return keyword, more specifically to return the parent object itself. Let's put that together:
function ScreenResizeTool() {
var self = this;
// method to instantiate the code is often stored in init property
this.init = function() {
window.addEventListener('resize', function() {
self.listen(); // self will refer to the prototype, not the window!
}, true);
};
return this;
}
ScreenResizeTool.prototype.listen = function() { // Dummy function
var h = window.innerHeight, w = window.innerWidth;
console.log('Resized to ' + w + ' x ' + h + '!');
};
Pretty easy huh? So we have our prototype now, but prototypes can't do anything if there's not an instance. So we create an instance of ScreenResizeTool and instantiate it with its init method:
var tool = new ScreenResizeTool();
tool.init();
// every time you resize the window now, a result will be logged!
You could also simply store the listen & init methods as private functions inside your constructor, and return them in an anonymous object:
function ScreenResizeTool() {
var listen = function() { ... };
var init = function() { ... };
// in this.init you can now simply call listen() instead of this.listen()
return {
listen: listen,
init: init
}
}
Check out the fiddle and make sure to open your console. Note that in this case I'd rather use the first function than the second (it does exactly the same) because prototypes are only useful if you have multiple instances or subclasses
The whole concept of this in JavaScript is a nightmare for beginners and in my code I usually try to avoid it as it gets confusing fast and makes code unreadable (IMHO). Also, many people new to JavaScript but experienced in object-oriented programming languages try to get into the whole this and prototype stuff directly though the don't actually need to (google JS patterns like IIFE for example as alternatives).
So looking at your original code:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg); // global function listen?
}, true);
}
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
First off, you probably mean addEventListener instead. In its callback you refer to listen but as a global variable which would look for it as window.listen - which doesn't exit. So you could think to do this:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
this.listen(currImg); // what's this?
}, true);
}
As you want to use the prototype.listen function of ScreenResizeTool. But this won't work either as the event listener's callback function is called with a different this and not the this that is your function scope.
This is where something comes in which makes most programmers cringe, you have to cache this, examples from code I've seen:
var _this = this;
var that = this;
var _self = this;
Let's just use the latter to be able to refer to the function within the event callback:
function ScreenResizeTool(currImg) {
var _self = this;
window.addEventListener('resize', function() {
_self.listen();
}, true);
}
Now this will actually work and do what you want to achieve: invoke the prototype.listen function of ScreenResizeTool.
See this JSFiddle for a working example: http://jsfiddle.net/KNw6R/ (check the console for output)
As a last word, this problem did not have anything to do with using jQuery or not. It's a general problem of JS. And especially when having to deal with different browser implementations you should be using jQuery (or another such library) to make your own code clean and neat and not fiddle around with multiple if statements to find out what feature is supported in what way.

The way of good scoping in JavaScript

I am not a really good JavaScript user but I can get things done with it. I am not proud of the code I have written in JavaScript, so I decided to change that. Here is my first step:
I am trying create my own library for a project and the below is the initial structure.
window.fooLib = {};
(function (foo) {
"use strict";
foo.doSomeStuff = function(param1) {
console.log(new AccommProperty(param1));
}
//some internal function
function AccommProperty(nameValue) {
var _self = this;
_self.name = nameValue;
}
}(fooLib));
I used immediately invoked function expression here to initialize my variable. In this case it is fooLib.
I am not sure if I should do some other things to make window.fooLib more safe. I mean it can be overridden by any other code which will run after my code if I understand JavaScript correctly.
What are your thoughts?
If you want to prevent overwriting your variables, you may use Object.defineProperty() with writable:false, configurable:false. In your case:
(function () {
"use strict";
var foo = {};
//some internal function
function AccommProperty(nameValue) {
var _self = this;
_self.name = nameValue;
}
foo.doSomeStuff = function(param1) {
console.log(new AccommProperty(param1));
}
Object.defineProperty(window, "foolib", {value:foo});
}());
Still, there is no good reason for that. It would need EcamScript 5.1 to work, and there are no shims around; maybe something with getters/setters to prevent overwriting with the = operator.
But also, there should be no need to make your library un-overwritable. Just don't use code on your site that overrides the lib. Or maybe someone even wants to overwrite your functions with another, better lib with the same interface?
If the question is about a library to be shared, with possible namespace conflicts to others, you may have a look at jQuery.noConflict.
Every JavaScript object can be overriden. This is the nature of JavaScript and it is impossible to change it. So you cannot make your code safe in that sense.
As for selfinvoked functions: you should use them when you want to have local variables but viisible to all your functions. So in your case AccommProperty is such variable. Defining doSomeStuff inside scope makes no difference unless doSomeStuff will use variables defined inside scope.
So when you want to hide variables from user and/or you need globals and you are affraid of name conflicts use selfinvoked functions.
I am not sure if I should do some other things to make window.fooLib more safe. I mean it can be overridden by any other code which will run after my code if I understand JavaScript correctly.
You could try making window.fooLib a local variable instead. Using closures and nested functions one can emulate a namespace where you can put all your data instead of putting it into the global scope or attaching it to window object:
(function() {
// all functions nested in foo() have access to fooLib.
fooLib = {}
fooLib.doSomeStuff = function(param1) {
console.log(param1);
console.log(fooLib);
}
//some internal function
function AccommProperty() {
console.log(fooLib);
}
}());
See Javascript Closures: Encapsulating Related Functionality for more details.

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

How do I call another function in the same javascript namespace?

I like to organize my javascript in namespace style like below. What I want to know : is there another (shorter?) way to call myFirstFunction() from mySecondFunction()? I tried this.myFirstFunction() and it's not working so maybe there's some kind of mysterious trick here that I don't know.
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
myNameSpace.myFirstFunction();
}
}
Thanks for your help as usual, people of SO! :)
As written in your example code, this.myFirstFunction() would work. Your code is likely simplified to illustrate your problem, so it would probably help to see the actual code to tell why it doesn't work with this.
One possible reason that it fails would be if the code where you call this.myFirstFunction() is inside a closure. If so, this would be a reference to the closing function, not your namespace and would therefore fail. See here for a contrived example based on your code to see what I mean. Again, having a look at the actual code would probably be helpful to diagnose what's going on.
Your suggestion to use 'this' should work. i.e.:
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
this.myFirstFunction();
}
}
Result:
myNameSpace.mySecondFunction() // "Hello World!".
If you want it to be shorter maybe you should consider the following pattern:
Javascript Design Pattern Suggestion
basically for your example:
var myNameSpace = (function()
{
function _myFirstFunction(){
alert("Hello World!");
}
function _mySecondFunction(){
_myFirstFunction();
}
return {
MyFirstFunction : _myFirstFunction,
MySecondFunction : _mySecondFunction
};
})();
I find this to be the cleanest pattern, also providing "private/public" variables in javascript that's otherwise pretty much impossible
In some cases the this keyword should work fine. If you explicitly call myNameSpace.mySecondFunction() then this.myFirstFunction() will execute as intended.
If you are using myNameSpace.mySecondFunction as an event handler it likely will not. In the case of an event handler you would need some way to refer to the namespace you want to use. A lot of JavaScript frameworks provide a way to define what the this keyword refers to. For example, in MooTools you can do myNameSpace.mySecondFunction.bind(myNameSpace) which will cause this to refer to myNameSpace inside mySecondFunction. If you are not using a framework you could make your event handler an anonymous function like:
document.getElementById('myId').addEventListener('click', function(e) {
myNameSpace.mySecondFunction.call(myNameSpace);
});
For more information on the call method I would refer to the MDC page for the call function or you could use apply which behaves similarly to call but passing an array of arguments for the second paramter rather than having a varargs like approach for additional parameters.
All of these suggestions are predicated on defining your namespace as #Harnish suggested:
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
this.myFirstFunction();
}
}
For more information about JavaScript function binding I'd highly suggest reading Justin's article on Function scope and binding in JavaScript
If you are attaching to event:
possible issue could be if you are attaching Namespace's function to event, like:
$(el).on("click", nameSpace.myFunc);
....
nameSpace = {
myFunc: function(){
this.anotherFunc();
}
}
that will throw error.
Solution 1
You may change this.anotherFunc() with nameSpace.anotherFunc()
Solution 2
You might change
$(el).on("click", nameSpace.myFunc);
// to ----->
$(el).on("click", function(){ nameSpace.myFunc(); } );

Categories

Resources