attache function to other function in javascript - javascript

I have a simple requirement, I need add the same code to hundreds of other JavaScript functions, the code can be executed at the end of the function, is there a handy way of doing it, like attach an function to another function dynamically, I think yes, because JavaScript is so powerful and too powerful, any ideas?
Note, I need dynamically assign new code or function to existing functions without change existing function's code, please give a solid solution, I can do it in hacky way, but no hacky way please!

The first method that comes to mind is simply create another function:
function primaryFunction() {
// ...
utilityMethod();
}
function otherPrimaryFunction() {
// ...
utilityMethod();
}
function utilityMethod() { ... }
Now utilityMethod() gets called from the end of each other primary function.
There's also a method which requires more code refactoring but is better in the long term: classes/prototypes.
Essentially, you have one "constructor" function which takes a number of parameters for the "class" and returns an class-like object:
function constructor(someClassField, anotherField) {
this.aField = someClassField;
this.fieldTwo = anotherField;
return this;
}
Now if you call this and pass some parameters, you get a class out:
var myClass = new constructor("1", "2");
myClass.aField == "1";
myClass.fieldTwo == "2";
So: If you define your utility method as above, then you can use this: for every primary function you instantiate a new instance of the constructor, with the final code looking like this:
function constructor(primaryFunction) {
this.function = primaryFunction;
this.call = function() {
this.function();
utilityMethod();
}
this.call();
return this;
}
function utilityMethod() { ... }
var primaryMethod = new constructor(function() { ... });
The creation of primaryMethod now automatically calls the primary function followed by the utility method, before returning the object so you can re-call both if you want to.

Related

Javascript - Call parent function and extend method

This is probably a stupid question, but is there way in Javascript (ES5 preferred) to "extend" a class function similar to how i can i extend a parent' function in PHP ?
Basicly, i have this class hierarchy from System -> Weapon -> Dual and i would like Dual to use the code from System.setState() and then do some more stuff.
Note i use pre ES6 syntax for my hierarchy.
function System(system){
this.setState = function(){
//do stuff
}
}
function Weapon(system){
System.call(this, system);
}
Weapon.prototype = Object.create(System.prototype);
function Dual(system){
Weapon.call(this, system);
this.setState = function(){ // this is the problem
System.prototype.setState(); // error - not defined
//Weapon.protoype.setState() doesnt work either
//do more stuff
}
}
Dual.prototype = Object.create(Weapon.prototype);
Because setState is an instance property of System it does not exist on System.proptotype so you can't call it using System.prototype.setState.call. If you want to call it in this case, just create an object from System like so
function Dual(system){
Weapon.call(this, system);
var parent = new System(system);
this.setState = function() {
parent.setState(); // done
}
}
Instance properties are duplicated on each individual object ( they don't share). Whereas, prototype properties will be shared among children( they are not duplicated on child classes). To make all System 's subclasses share setState function, add it to System 's prototype
function System (arg) { ... }
System.prototype.setState = function () {...}
Now in your child classes, you can do
function Dual(system){
Weapon.call(this, system);
this.setState = function() {
System.prototype.setState.call(this); // done
}
}
First, you should set your instance methods on the prototype:
System.prototype.setState = function() {
// your stuff
}
This will improve performance and allow you to inherit the method without constructing a System instance.
Then, you just need to call System's version of setState on the right object (the instance of Dual) instead of calling it on System.prototype:
Dual.prototype = Object.create(Weapon.prototype, {
'setState': { value: function(){
System.prototype.setState.call(this) // fixed
// other stuff
}}
})

Call a method after a callback and an event

I have a module with four functions that call one after the other. I am trying to follow the Revealing Module Pattern. One of the functions is public, the remaining are private. It goes like this:
publicMethod is called from another module
queryNames is called from publicMethod
execute(parameters, callback?, errback?) is called from queryNames
addNamesList is called as the callback? argument of execute
Several dijit/form/CheckBox's are created and the method querySegments is triggered onChange
querySegments needs to call a method of an object created in publicMethod.
The problem is in step 6, I can't reach the object created in step 1.
I have tried to use dojo hitch to define the callback? argument in step 3, but I can't get it to work. I tried putting this in its first argument, but even then I can't reach the required scope to call addNamesList.
Here is some code to demonstrate this issue.
define([
'dojo/dom',
'dijit/form/CheckBox',
'esri/layers/ArcGISDynamicMapServiceLayer',
'esri/tasks/query',
'esri/tasks/QueryTask',
'dojo/_base/lang'
],
function (
dom,
CheckBox,
ArcGISDynamicMapServiceLayer,
Query, QueryTask,
lang
) {
// ***************
// private methods
// ***************
// fetch names and call addNamesList to put the list in place
var queryNames = function (map, mapLayer) {
// new QueryTask(url, options?)
var queryTask = new QueryTask("url")
var query = new Query()
// execute(parameters, callback?, errback?)
// this callback passes an argument called featureSet
queryTask.execute(query, lang.hitch(map, "addNamesList", mapLayer), function(error) {console.log(error)})
}
// callback function of queryNames
var addNamesList = function (mapLayer, featureSet) {
console.log('addOplist')
var namesCount = featureSet.features.length
for (var i = 0; i <namesCount; i++) {
// work
var cbox = new CheckBox({
id: "cbox_" + i,
value: featureSet.features[i].attributes["someID"],
checked: false,
onChange: function (evt) {
querySegments(this.value, mapLayer)
}
})
cbox.placeAt("someDiv" + i, "first")
}
}
// triggered by the checkbox event
var querySegments = function (name, mapLayer) {
// build the query
var queryStatement = "someID = " + name
var layerDefinitions = [queryStatement]
// call a method of mapLayer
mapLayer.setLayerDefinitions(layerDefinitions)
}
// **************
// public methods
// **************
var publicMethod = function (map) {
var mapLayer = new ArcGISDynamicMapServiceLayer('restURL')
map.addLayer(mapServiceLayer)
queryNames(map, mapLayer)
return mapLayer
}
return {
publicMethod: publicMethod
}
}
)
You can see a more detailed explanation and a working example on this other (and more broad) question that I have put on Code Review.
I am new to JavaScript and I guess I still have a lot of issues with scoping, closures and callbacks.
I will deeply appreciate any input, including how to improve this question.
Edit
With this current implementation (with dojo hitch), no error is thrown. The method addNamesList is not called (nor errback, which I also don't understand why). I think this is because addNamesList is not on map's (hitch first argument) namespace. I tried to put this instead, but it makes no difference.
Before I decided to use hitch, the code looked like this:
var queryNames = function (map, mapLayer) {
...
queryTask.execute(query, addNamesList)
}
var addNamesList = function (featureSet) {
...
...
...
querySegments(this.value, mapLayer)
}
but then I couldn't reach mapLayer inside the method triggered by the check box event. It would throw Uncaught ReferenceError: mapLayer is not defined. That is why I tried to use hitch.
Javascript is asynchronous, so pretty much data coming from db, http requests or whatever is returned via callbacks. Here's what happens in your code:
public method calls queryNames
queryNames call addNamesList of map asynchronously and returns nothing
public method takes back control, meanwhile some stuff is going on with the addNamesList
mapLayer is returned untouched while some stuff is still going on in the background
So, to avoid this, you should return data from public method via callback, so you pass callback as the second parameter to the public method, then to the querySegments. Then, in the success callback of query, when you finally get the result ready, you do:
callback(mapLayer);
So, everything you should do is to pass this callback as deep as needed to the place where you have your mapLayer ready (so you've done with it everything you wanted), and then do a callback(mapLayer);.
This and this would probably explain better.
Best regards, Alexander

passing an object to a function in javascript

This might seem like a noob question but I'm not sure what to do. I have function with 2 variables.
function someInfo(myVar1,myVar2)
{
this.lmyVar1=myVar1;
this.myVar2=myVar2;
}
myInstance=new someInfo("string1","string2");
function drawVariables(){
document.write(myInstance.myVar1);
document.write(myInstance.myVar2);
}
I want to use the same drawVariable() for multiple instances. I just can't figure out how the exact syntax for that. How can I make drawVariable() use a different instance of someInfo without repeating anything? Is there a simple example or tutorial I can follow?
Add an argument to the definition of function drawVariables. In the code below, this argument is called info. Now you can use info as your object inside the drawVariables function, and while calling drawVariables function, you can pass whatever instance you want to pass it. drawVariables function would now work with whatever instance you pass it while calling.
function someInfo(myVar1,myVar2)
{
this.myVar1=myVar1;
this.myVar2=myVar2;
}
// Create two separate instances
myInstance=new someInfo("string1", "string1");
myInstance2 = new someInfo("string2", "string2");
// info is the argument that represents the instance passed to this function
function drawVariables(info){
alert(info.myVar1 + ", " + info.myVar2);
}
// Call the function twice with different instances
drawVariables(myInstance);
drawVariables(myInstance2);
See http://jsfiddle.net/WLHuL/ for a demo.
function drawVariables(instance){
document.write(instance.myVar1);
document.write(instance.myVar2);
}
Would it make sense for you to do it this way?
function someInfo(myVar1, myVar2)
{
this.lmyVar1 = myVar1;
this.myVar2 = myVar2;
this.drawVariables = function ()
{
document.write(this.lmyVar1);
document.write(this.myVar2);
}
}
function Test()
{
var obj1 = new someInfo("aaa", "bbb");
var obj2 = new someInfo("xxx", "zzz");
obj1.drawVariables();
obj2.drawVariables();
}

Javascript Class Inheritance

Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.

jQuery and Object Oriented Javascript - How do I do it properly?

I'm quite experienced with jQuery but I'm just getting into object oriented Javascript and want to make sure I'm doing it right.
Here's an example of what I'm doing so far... I'd appreciate tips/advice on how I can improve my approach:
// create a class for collapsable fieldsets
window.CollapsableFieldset = (function() {
function CollapsableFieldset(jQObject) {
// look for all the legend tags within this fieldset
jQObject.find("legend").click(function(event) {
// add or remove the "open" class to the fieldset
return jQObject.toggleClass("open");
});
}
return CollapsableFieldset;
})();
jQuery(document).ready(function() {
return $("fieldset.collapsable").each(function(i, element) {
return new CollapsableFieldset($(element));
});
});
So basically I'm creating a class that defines the behaviour I'm looking for and then in the jQuery block, I'm selecting each object that matches and creating a new instance of the class with that object as an argument.
This is not object-oriented javascript.
Before getting into prototyping, let me make one remark: your use of a closure in CollapsableFieldset is unnecessary and cumbersome. Indeed, you do not have any variables in the (function(){})() pattern. You may rewrite, without any loss, to:
window.CollapsableFieldset = function (jQObject) {
jQObject.find('legend').click(function(event) {
return jQObject.toggleClass('open');
});
}
Now, you create classes in javascript using a function constructor, in which the "this" keyword defines the object that a "new" statement will yield:
function CollapsableFieldset (jQObject) {
// This is the function constructor.
this.field = jQObject;
}
CollapsableFieldset.prototype.findAndToggle = function (tag, cssclass) {
// This defines a method "findAndToggle" on the CollapsableFieldset type.
this.field.find(tag).click(function(event) {
return this.field.toggleClass(cssclass);
});
};
jQuery(document).ready(function() {
return $("fieldset.collapsable").each(function(i, element) {
var colFieldset = new CollapsableFieldset($(element));
return colFieldset.findAndToggle('legend', 'open');
});
});
Please bear in mind that object-oriented javascript is not always the optimal solution.
One option would be to use Jquery UI's widget factory and encapsulate behaviour in widgets.
http://wiki.jqueryui.com/w/page/12138135/Widget-factory
You are using an immediately called function expression for no good reason, your code is functionally equivalent to:
function CollapsableFieldset(jQObject) {
// look for all the legend tags within this fieldset
jQObject.find("legend").click(function(event) {
// add or remove the "open" class to the fieldset
return jQObject.toggleClass("open");
});
}
The "constructor" doesn't return the object created when it is called with new, no advantage it taken of prototype inheritance so no point to using new.
The object returned by the function isn't assigned to anything, so what's the point of returning anything?
> jQuery(document).ready(function() {
> return $("fieldset.collapsable").each(function(i, element) {
> return new CollapsableFieldset($(element));
> });
> });
Where does the object returned by new CollapsableFieldset(...) go?
Incidentally, there is no point to creating global variables using:
window.varName...
when
var varName ...
does it so much better.

Categories