Why would this code lead to infinite loop/recursive - javascript

I have the following javascript code:
var original_selectCallback = window.selectCallback;
var selectCallback = function(variant, selector) {
original_selectCallback(variant, selector);
console.log(variant + " " + selector);
};
Somehow, the 2nd line:
var selectCallback = function(variant, selector)
leads to a recursive call to selectCallback. My understanding is that I am redefining the selectCallback function. So why would this be a recursive call?
Any suggestions/advice is appreciated.

There is nothing inherently wrong with the code you've posted:
var selectCallback = function() { console.log('original'); };
var original_selectCallback = selectCallback;
var selectCallback = function() {
original_selectCallback();
console.log('wrapper');
};
selectCallback();
The error is likely in the initial definition of window.selectCallback, which is not shown here.

There is no recursion here and that is quite easy to demonstrate:
let someFn = function () { console.log('original'); };
let originalSomeFn = someFn;
someFn = function () {
originalSomeFn();
console.log('decorated');
};
someFn();
However, the original function may be involved in a recursive algorithm somewhere and your wrapper function might not entirely fulfill the proper behavioral contract of the original function.
To make sure here's how you should be wrapping the original function. I also opted for a closure to avoid a global originalSomeFn variable.
let someFn = function () {
console.log('original');
return 1;
};
someFn = (function (originalSomeFn) {
return function () {
let result = originalSomeFn.apply(this, arguments);
console.log('decorated');
return result;
};
})(someFn);
console.log(someFn());

Let's check your code:
var original_selectCallback = window.selectCallback;
creates new variable that refers to function selectCallback.
var selectCallback = function(variant, selector) {
creates window.selectCallback function (because it's in window scope).
Now inner part of function
original_selectCallback(variant, selector);
will call window.original_selectCallback that is same as window.selectCallback that is same function where you are now.
So you make recursion with selectCallback that never ends.

Related

javascript class with event methods

My goal is to make a class with some chained functions, but I'm stuck and hoping for some help. This is what I got:
robin = new batman("myiv");
var batman = (function() {
var me = this;
function batman(id){
me._id=id;
document.getElementById(id).addEventListener('mousemove', me.mouseMoving.bind(me),true);
}
this.mouseMoving = function(){
document.getElementById(me._id).style.background="orange";
}
return batman;
}
And this pseudo code is what I am aiming to get. Basically, pass in the ID of an element in my HTML and chain functions to it such as onclick etc, and whatever code inside there, runs. as in example, changing background colors.
Is it possible?
superman("mydiv"){
.onmouseover(){
document.getElementById(the_id).style.background="#ffffff";
},
.onmouseout(){
document.getElementById(the_id).style.background="#000000";
},
etc...
}
edit: updated with missing code: "return batman;"
You can do method chaining by returning the current object using this keyword
var YourClass = function () {
this.items = [];
this.push = function (item) {
if (arguments) {
this.items.push(item);
}
return this;
}
this.count = function () {
return this.items.length;
}
}
var obj = new YourClass();
obj.push(1).push(1);
console.log(obj.count())
Working sample
https://stackblitz.com/edit/method-chaining-example?file=index.js

Call a returned function from outside its function

I'm trying to call a function that's returned from a function. Here's what I mean:
myFunction.something; // (Wrong)
function myFunction() {
return {
something: function() {
...
}
};
}
When I try calling myFunction.something nothing happens. How can I call a returned function outside of its function?
JSFiddle
var index = 0;
var animID = requestAnimationFrame(myFunction.something);
function myFunction() {
return {
something: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(myFunction.something);
}
};
}
I would first of all recommend using descriptive variable names; utils rather than myFunction, and incrementFrame rather than something, for example. I would second of all recommend reconsidering your approach to code organization and simply putting all of your helper functions directly in an object, then referencing that object:
var index = 0;
var animID = requestAnimationFrame(utils.incrementFrame);
var utils = {
incrementFrame: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(utils.incrementFrame);
}
}
There are a few differences between these approaches, some of them frustratingly subtle. The primary reason I recommend using an object for organization rather than a function which returns an object is because you don't need to use a function for organization; you are unnecessarily complicating your code.
myfunction is not the object that you get from calling myfunction(), it's the function itself and does not have a .something method.
You could call it again (as in myfunction().something()), but a better approach would be to store a reference to the object you've already created:
function myFunction() {
var index = 0;
var o = {
something: function() {
index++;
console.log(index);
if (index < 5) requestAnimationFrame(o.something);
// btw you don't need to cancel anything once you reach 5, it's enough to continue not
}
};
return o;
}
myFunction().something();
Alternatively you might want to drop the function altogether, or use the module pattern (with an IIFE), as you seem to use it like a singleton anyway.
Try this:
myFunction().something()
myFunction() calls the myFunction function
them we use the dot notation on the returned value (which is an object) to find the something member of it
that member is a function too, so add another set of brackets () to call it
Call function after writing it
var index = 0;
function myFunction() {
return {
something: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(myFunction().something);
}
};
}
var animID = requestAnimationFrame(myFunction().something);

How to dynamically change a function

I have a function that has already been assigned and I would like to append some additional script. When I display the function like:
var func = obj.when_clicked;
alert(func);
The following is displayed:
function object_123(){
object_123_Action();
}
The type is a "function" and the function is executed elsewhere which I cannot change, the code is:
this.when_clicked();
I need to modify the function by appending my own code so it becomes:
function object_123(){
object_123_Action();
my_clicked(obj);
}
Then add that back and over write the when_clicked function.
It does work if I manually add the code like:
obj.when_clicked = function object_123(){object_123_Action();my_clicked(obj);};
However, I do not know what is in the function initially, all I want is to append the:
my_clicked(obj);
I do need the obj to be the actual object of interest which is obj.
You can wrap it:
var f = this.when_clicked;
this.when_clicked = function() {
// Call the original
var rv = f.apply(this, arguments);
// your code here
// Return the original function's return value
return rv;
};
Function#apply calls the original with the specified this flag. arguments is provided by the JavaScript engine: It's a pseudo-array of the arguments your function was called with, so the above just passes all of them on.
Be sure to think about what it means if your function throws an exception, and catch them if you want to suppress them.
If you do this often, you can giev yourself a utility function:
function wrapFunction(f, wrapper) {
return function() {
var rv = f.apply(this, arguments);
wrapper.apply(this, arguments);
return rv;
};
}
Then
this.when_clicked = wrapFunction(this.when_clicked, function() {
// Your code here
});
Or if you want access to the original's return value, potentially changing it:
function wrapFunction(f, wrapper) {
return function() {
var rv = f.apply(this, arguments);
rv = wrapper.call(this, rv, arguments);
return rv;
};
}
Then
this.when_clicked = wrapFunction(this.when_clicked, function(rv, args) {
// Your code here, using `rv` and `args`, which is a pseudo-array
// Potentially update `rv`
return rv;
});
You can store your functions in an array on the object.
Then, loop though the functions in another function and execute them.
var myObj = { 'myfunctions': [ ] };
and to add functions:
myObj.myFunctions.push (function () { /*function code here*/ });
Or if you already have a named function:
myObj.myFunctions.push (nameOfFunction);
And to call all the functions, use this function (don't add this function to myObj)
function executeMyFunctions (myObj) {
for (var i = 0; i < myObj.myFunctions.length; i++) {
myObj.myFunctions[i]();
}
}
Used this answer on another question of #peter. Meant to post here.

Javascript concatenate a function similar to how text can be added

In javscript we can do this
var text = "the original text";
text+=";Add this on";
If a library has a function already defined (e.g)
//In the js library
library.somefunction = function() {...};
Is there a way to add something on so that I can have two functions run?
var myfunction = function() {...};
Something like:
library.somefunction += myfunction
So that both myfunction() and the original library.somefunction() are both run?
You can use this kind of code (leave scope empty to use default scope):
var createSequence = function(originalFn, newFn, scope) {
if (!newFn) {
return originalFn;
}
else {
return function() {
var result = originalFn.apply(scope || this, arguments);
newFn.apply(scope || this, arguments);
return result;
};
}
}
Then:
var sequence = createSequence(library.somefunction, myFunction);
I think what you want to create is a Hook (function) - you want to call library.somefunction but add a bit of your own code to run before. If that's the case, you can make your myfunction either call or return the library function after it's done with your bit of code.
var myfunction = function() {
// your code
// ...
return library.somefunction();
}

Can you alter a Javascript function after declaring it?

Let's say I have var a = function() { return 1; }. Is it possible to alter a so that a() returns 2? Perhaps by editing a property of the a object, since every function is an object?
Update: Wow, thanks for all the responses. However, I'm afraid I wasn't looking to simply reassign a variable but actually edit an existing function. I am thinking along the lines of how you can combine partial functions in Scala to create a new PartialFunction. I am interested in writing something similar in Javascript and was thinking that the existing function could perhaps be updated, rather than creating an entirely new Function object.
You can do all kinds of fun stuff with javascript, including redefining functions:
let a = function() { return 1; }
console.log(a()); // 1
// keep a reference
let old = a;
// redefine
a = function() {
// call the original function with any arguments specified, storing the result
const originalResult = old.apply(old, arguments);
// add one
return originalResult + 1;
};
console.log(a()); // 2
Voila.
Edit: Updated to show this in a crazier scenario:
let test = new String("123");
console.log(test.toString()); // logs 123
console.log(test.substring(0)); // logs 123
String.prototype.substring = function(){ return "hahanope"; }
console.log(test.substring(0)); // logs hahanope
You can see here that even though "test" is defined first, and we redefine substring() afterwards, the change still applies.
Side note: you really should reconsider your architecture if you're doing this...you're going to confuse the crap out of some poor developer 5 years down the road when s/he's looking at a function definition that's supposed to return 1, but seems to always return 2....
So you want to modify the code of a function directly, in place, and not just reassign a different function to an existing variable.
I hate to say it, but as far as I have been able to figure it out - and I have tried -, it can't be done. True, a function is an object, and as such it has methods and properties which can be tweaked and overwritten on the object itself. Unfortunately, the function body is not one of them. It is not assigned to a public property.
The documentation on MDN lists the properties and methods of the function object. None of them gives us the opportunity to manipulate the function body from the outside.
That's because according to the spec, the function body is stored in the internal [[Code]] property of the function object, which can't be accessed directly.
I used something like this to modify an existing function whose declaration was not accessible to me:
// declare function foo
var foo = function (a) { alert(a); };
// modify function foo
foo = new Function (
"a",
foo.toSource()
.replace("alert(a)", "alert('function modified - ' + a)")
.replace(/^function[^{]+{/i,"") // remove everything up to and including the first curly bracket
.replace(/}[^}]*$/i, "") // remove last curly bracket and everything after<br>
);
Instead of toSource() you could probably use toString() to get a string containing the function's declaration. Some calls to replace() to prepare the string for use with the Function Constructor and to modify the function's source.
let a = function() { return 1; }
console.log(a()) // 1
a = function() { return 2; }
console.log(a()) // 2
technically, you're losing one function definition and replacing it with another.
How about this, without having to redefine the function:
var a = function() { return arguments.callee.value || 1; };
alert(a()); // => 1
a.value = 2;
alert(a()); // => 2
I am sticking to jvenema's solution, in which I don't like the global variable "old". It seems better to keep the old function inside of the new one:
function a() { return 1; }
// redefine
a = (function(){
var _a = a;
return function() {
// You may reuse the original function ...
// Typical case: Conditionally use old/new behaviour
var originalResult = _a.apply(this, arguments);
// ... and modify the logic in any way
return originalResult + 1;
}
})();
a() // --> gives 2
All feasible solutions stick to a "function wrapping approach".
The most reliable amongst them seems to be the one of rplantiko.
Such function wrapping easily can be abstracted away. The concept / pattern itself might be called "Method Modification". Its implementation definitely belongs to Function.prototype. It would be nice to be backed
one day by standard prototypal method modifiers like before, after, around, afterThrowing and afterFinally.
As for the aforementioned example by rplantiko ...
function a () { return 1; }
// redefine
a = (function () {
var _a = a;
return function () {
// You may reuse the original function ...
// Typical case: Conditionally use old/new behaviour
var originalResult = _a.apply(this, arguments);
// ... and modify the logic in any way
return originalResult + 1;
};
})();
console.log('a() ...', a()); // --> gives 2
.as-console-wrapper { min-height: 100%!important; top: 0; }
... and making use of around, the code would transform to ...
function a () { return 1; }
console.log('original a ...', a);
console.log('a() ...', a()); // 1
a = a.around(function (proceed, handler, args) {
return (proceed() + 1);
});
console.log('\nmodified a ...', a);
console.log('a() ...', a()); // 2
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function(d){function f(a){return typeof a==e&&typeof a.call==e&&typeof a.apply==e}function g(a,b){b=null!=b&&b||null;var c=this;return f(a)&&f(c)&&function(){return a.call(b||null!=this&&this||null,c,a,arguments)}||c}var e=typeof d;Object.defineProperty(d.prototype,"around",{configurable:!0,writable:!0,value:g});Object.defineProperty(d,"around",{configurable:!0,writable:!0,value:function(a,b,c){return g.call(a,b,c)}})})(Function);
</script>
This is a Clear Example based on a control timepicker eworld.ui
www.eworldui.net
Having a TimePicker eworld.ui where JavaScript is unreachable from outside, you can't find any js related to those controls. So how can you add a onchange event to the timepicker ?
There is a js function called when you Select a time between all the options that the control offer you. This function is: TimePicker_Up_SelectTime
First you have to copy the code inside this function.
Evaluate...quikwatch...TimePicker_Up_SelectTime.toString()
function TimePicker_Up_SelectTime(tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) {
document.getElementById(tbName).value = selTime;
if(lblName != '')
document.getElementById(lblName).innerHTML = selTime;
document.getElementById(divName).style.visibility = 'hidden';
if(enableHide)
TimePicker_Up_ShowHideDDL('visible');
if(customFunc != "")
eval(customFunc + "('" + selTime + "', '" + tbName + "');");
eval(postbackFunc + "();");
}
Now
Using the code that you have saved before reassign the same source code but add whatever you want..
TimePicker_Up_SelectTime = function (tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) {
document.getElementById(tbName).value = selTime;
if (lblName != '')
document.getElementById(lblName).innerHTML = selTime;
document.getElementById(divName).style.visibility = 'hidden';
if (enableHide)
TimePicker_Up_ShowHideDDL('visible');
if (customFunc != "")
eval(customFunc + "('" + selTime + "', '" + tbName + "');");
eval(postbackFunc + "();");
>>>>>>> My function >>>>> RaiseChange(tbName);
}
I've added My Function to the function so now I can simulate an onchange event when I select a time.
RaiseChange(...) could be whatever you want.
If you're debugging javascript and want to see how changes to the code affects the page, you can use this Firefox extension to view/alter javascripts:
Execute JS firefox extension:
https://addons.mozilla.org/en-US/firefox/addon/1729
You can change functions like other objects
var a1 = function(){return 1;}
var b1 = a1;
a1 = function(){
return b1() + 1;
};
console.log(a1()); // return 2
// OR:
function a2(){return 1;}
var b2 = a2;
a2 = function(){
return b2() + 1;
};
console.log(a2()); // return 2
Can you not just define it again later on? When you want the change try just redefining it as:
a = function() { return 2; }
const createFunction = function (defaultRealization) {
let realization = defaultRealization;
const youFunction = function (...args) {
return realization(...args);
};
youFunction.alterRealization = function (fn) {
realization = fn;
};
return youFunction;
}
const myFunction = createFunction(function () { return 1; });
console.log(myFunction()); // 1
myFunction.alterRealization(function () { return 2; });
console.log(myFunction()); // 2

Categories

Resources