How does one pass a variable to one function and then return a value to another function?
I have simple example here where I am trying to work it out. The first function accepts the argument 'currpage', returns it to the second function correctly as a number, but then when I call the second function, console.log shows NaN. Not sure what I might be doing wrong.
$(document).ready(function () {
var currpage = 1;
$('p').click(function () {
var xyz = passfrom(currpage); //pass var to function and return value
console.log(xyz); //returns correct value
var abc = passto();
console.log(abc); //NaN
})
})
function passfrom(currpage) {
var newpage = parseInt(currpage) * 1000;
return newpage;
}
function passto() {
var newcurr = passfrom();
var newcurr = newcurr * 1000;
console.log(typeof (newcurr)); //number
return newcurr;
}
How does one pass a variable to one function and then return a value to another function?
A variable is just a little container where you store a value. When you pass a variable to a function, you really pass the value of that variable to it. So in your case:
passfrom(curpage);
and
passfrom(1);
are the same.
Within a function, variable names are used to access these values. These names are totally independent of whatever name was attached to the value outside the function (if it even had a name). They are more like aliases. To distinguish them from variables, we call them parameters. So this one:
function passfrom(currpage) {
var newpage = parseInt(currpage)*1000;
return newpage;
}
and this one:
function passfrom(myownname) {
var newpage = parseInt(myownname)*1000;
return newpage;
}
are exactly the same. And if we were to write out what actually happens, we'd get this:
// var xyz = passfrom(currpage);
var xyz = value-of(passfrom(value-of(currpage))
So all you have to do to pass a value to some function, is to make sure that it has such a parameter name available by which it can use that value:
function passto(myalias) {
console.log(myalias);
}
passto(xyz); // writes 1000 to the console.
The above is the actual answer to your question.
To make things a little bit more complicated, there are two more things to take into account:
Scope. The parameter names only work within your function. If they are the same as some variable name outside the function, that outside variable is hidden by the parameter. So:
var currpage = 1;
function plusOne(currpage) { curpage += 1; }
plusOne(currpage);
console.log(currpage); // 1, as the variable currpage was hidden
function plusTwo(othername) ( currpage += 2; }
plusTwo(currpage);
console.log(currpage); // 3, as currpage was not hidden
This all works for strings, integers, and other simple types. When you're dealing with more complex types, the parameter name isn't an alias for the value passed to the function, but for the location of the original value. So in that case, whatever you do with the parameter within the function will automatically happen to the variable outside the function:
var arr = [ 0, 1 ];
function plusOne(somearr) { somearr[0] += 1; }
plusOne(arr);
console.log(arr[0]); // 1, as somearr references arr directly
This is called "pass-by-value" and "pass-by-reference."
You're dealing with two different currpage variables, causing one to be undefined when you try to perform arithmetic on it, resulting in a NaN result. See my inline code comments below for further explanation:
$(document).ready(function() {
var currpage=1; // Local to this function, because of the var keyword.
...
})
}) // I'm assuming this extra closing brace and paren is a typo.
// Otherwise, your code example has a syntax error or is incomplete.
function passfrom(currpage) {
// currpage is the name of the parameter to passfrom.
// It happens to have the same name as a local var in
// the document.ready callback above, but the two are
// not the same.
var newpage = parseInt(currpage)*1000;
return newpage;
}
function passto() {
// passfrom is called with an implicit 'undefined' argument.
// Thus, undefined will be used in the arithmetic ops and produce NaN.
var newcurr = passfrom();
// Don't need the var keyword below.
var newcurr = newcurr * 1000;
console.log(typeof(newcurr)); //number
return newcurr;
}
You need to make the same currpage variable accessible from both passfrom and passto by putting it in a higher/more global scope or move those functions into the same scope that the original currpage is in. Something like this:
var currpage;
$(document).ready(function () {
$('p').click(function () {
var xyz = passfrom(1); //pass var to function and return value
console.log(xyz); //returns correct value
var abc = passto();
console.log(abc); //NaN
})
})
// Rename the param so there isn't a naming conflict.
function passfrom(currpageParam) {
// If the param is a number, reset the global var.
if (typeof currpageParam == 'number') { currpage = currpageArg; }
var newpage = parseInt(currpage) * 1000;
return newpage;
}
function passto() {
var newcurr = passfrom();
newcurr = newcurr * 1000;
console.log(typeof (newcurr)); //number
return newcurr;
}
Be careful though. You'll probably want to take steps to protect your currpage var from outside modification. Also, I suspect that there's a better way to do what you're trying to do, but it isn't clear exactly what that is, so I can't suggest anything.
Related
So I'm writing a program in JavaScript in which several of the values within an array are dependent on the outcome of a function. This function is used elsewhere in the code as well, and works fine there.
The code looks something like this:
var Multiplier;
$("#button").on("click", function() { foo(); });
function foo() {
x = document.getElementById("select").value;
setMultiplier(x);
setName(x);
$("#select").hide();
$("#page").show();
}
function setMultiplier(q) {
if (q === "Option 1") {
Multiplier = 1;
return Multiplier;
} else if (q === "Option 2") {
Multiplier = 100;
return Multiplier;
}
}
function mult(base) {
var z;
z = base * Multiplier;
return z;
}
The problem I am having has nothing to do with the setName() as far as I can tell, because that function works fine. Also, Multiplier has the proper value elsewhere. However, when it is used within an array found later in the program, it gives a value of undefined and when I type it into the console it also says Multiplier is not defined. The same thing happens with the mult() function both in the array and in the console, but it works fine elsewhere. Am I missing something here?
EDIT:
So later in the program, mult(base) is called upon in two types of contexts:
1. Within a function (which works) like so:
function blah() {
var s = "<div>$" + mult(75) + "</div>";
s += "<div>$" + mult(150) + "</div>";
document.getElementById("mydiv").innerHTML = s;
}
2. Within an array (which doesn't work) like so:
var array = [["String1", mult(60), 0, [mult(2), mult(5)], mult(30)],
["String2", mult(100), 0, [mult(10), mult(25)], mult(50)]];
You can, and you are.
The issue is that you initialize properties with mult() calls only once, at the time you declare properties. TemplateName is only initialized after someone makes clicky on the UI: before it's initialized its value is null, which doesn't play nice with numbers.
JS won't magically go back and re-initialize properties, or re-run mult every time you access on of its value: when you declare properties and set all those values it's doing it with whatever mult returns at the time properties is declared.
function decode(s) {
decode = function (s) {
return s + '!'
}
return s+'?'
}
console.log(decode("asd"))//outputs asd?
console.log(decode("qwe"))//outputs qwe!
This code will replace function body from within a function. But unfortunately this is not very clean. Because first decode is module function scope and second decode is global variable scope. Is there another, cleaner way to replace the function body from inside the function?
The usage case example would be that the first time function called it might need to initialize some data. And subsequent calls will only do a decoding.
You can use something like this to make a variable that tracks the number of times the function has been called and do some initialization the first time it's called:
var decode = (function(){
var numCalls = 0;
var potatos;
return function(){
if(numCalls++ == 0){
// Initialize some stuff here
potatos = 7;
}
// Use stuff here
console.log(potatos);
potatos += 3;
}
})();
The pattern you are using is well known as Self-Defining Functions (or possibly lazy function definition).
Instead, why not use an object with a constructor function,
var Decode = function(s){
var that = this;
that.property = s;
that.decode = function(){
return that.property + "!";
};
};
var decode = new Decode("asd");
decode.property //"asd"
decode.decode() //"asd!"
var sc = new stuCore();
function stuCore() {
this.readyPages = [];
this.once = true;
var self = this;
// gets called asynchronously
this.doPrepPage = function (page){
if(self.once == true){
// still gets executed every time, assignment fails
self.once = false;
doSomeStuffOnce();
}
};
this.addReadyPage = function (pageid) {
console.log("readypage called");
this.readyPages.push(pageid);
if (!$.inArray(pageid, self.readyPages) != -1) {
this.doPrepPage(pageid);
}
};
}
why does this assignment fail? I thought I knew the basics of js, but I'm stumped by this. And furthermore what would be a possible solution? call a constructor first and set the variable there?
EDIT:
gets called like this in some other script:
sc.addReadyPage(self.id);
The jQuery.inArray function will return the index in the containing array for the given value. Your script pushes pageid into this.readyPages before checking whether it exists in self.readyPages. this.readyPages and self.readyPages are the same array reference, so the result will always be zero or greater, so the condition that calls doPrepPage will never run.
You could try switching their order around:
this.addReadyPage = function (pageid) {
console.log("readypage called");
if ($.inArray(pageid, self.readyPages) != -1) {
this.readyPages.push(pageid);
this.doPrepPage(pageid);
}
};
(edit: Removed the additional !, thanks #chumkiu)
If I understand correctly you're calling this.doPrepPage as <insert variable name here>.doPrepPage?
If this is the case then your var self passes through to the anonymous function and is stored there, so everytime you call this.doPrepPage it takes the local variable of self.
Try setting self to a global variable, this way it will permanently modify self so each time this.doPrepPage is called it uses the updated variable.
function updateServerList() {
var i;
for (i=0; i < servers.length; i++) {
var server = servers[i];
var ip = server['serverIp']
var html = constructServer(i);
var divId = '#server' + ip.replace(new RegExp("\\.", "mg"), "-");
var visible = $(divId).find(".server_body").is(":visible");
var div = $(divId);
div.html(html);
// Set div class.
var prevState = div.attr('class').substring(7)
if (prevState != server['state']) {
if (server['state'] == 'ok') {
console.debug(server);
div.slideUp('fast', function(server) {
$(this).removeClass();
$(this).addClass('server_ok');
var id = ipToId[server['serverIp']];
console.debug(id);
if (id == 0) {
adjacentIp = servers[1]['serverIp'];
adjacentDivId = '#server' + adjacentIp.replace(new RegExp('\\.', 'g'), '-');
$(adjacentDivId).before(this);
}
}).delay(1000);
div.slideDown();
}
}
}
console.debug shows server as being defined, but inside the anonymous function, server is not defined. What am I going wrong?
because server is an argument to the function, its masking the value of the server at the higher level. You need to either pass server to the function, or remove the function argument. I would do the latter, as slideUp doesn't give you a way to pass arguments. You could do it but its needlessly complicated; it would look something like the following
div.slideUp('fast', (function(server) {
return function(){
// your stuff here, server is now 'closed in', i.e. in a closure
}
})(server)); // <-- this server is the current value in the loop
what you are doing here is invoking a new function right away, passing in the argument server, and returning a new function that receives that value.
var server = servers[i];
var prevState = div.attr('class').substring(7);
if (prevState != server['state']) {
if (server['state'] == 'ok') {
console.debug(server);
div.slideUp('fast', function() {
...
var id = ipToId[server['serverIp']];
}
}
Inside your anonymous function, "server" is still within the function scope. No need to pass it in as an argument.
The Quick Fix
// ...
div.slideUp('fast', function() { // `server` argument removed
// ...
});
The Explanation
There is no need to pass server to the function. The anonymous function "closes" over the server variable.
This is merely a function declaration:
function (server) {...}
You aren't passing anything to the function yet, as it isn't being invoked yet! The (server) bit
in a function declaration simply lets you name the arguments to your function. Only when you invoke the function can you pass arguments:
var name = "Jill";
var showName = function (name) {
alert(name);
};
showName("Jack"); // alert box shows "Jack"
showName(); // alert box shows "undefined"
So, when you declare that the name of the first argument to your anonymous function is server, there is a name conflict which prevents the original from being accessible; the server in your anonymous function is whatever slideUp passes as the first argument, which, according to the documentation, is nothing, so server is now undefined.
If this is confusing (and I suspect it is), I would suggest reading about javascript closures. Here's a good place to get started.
Fun fact: you can actually access arguments, in order, without having any explicit names, by using Javascript's built in arguments array object inside a function:
var sum = function () {
var i, total = 0;
for(i = 0; i < arguments.length; ++i) {
total = total + arguments[i];
}
return total ;
};
alert(sum(1,2,3)); // Displays "6"
alert(sum(1,2,3,4)); // Displays "10"
alert(sum(1,0,2,3)); // Displays "6"
alert(sum()); // Displays "0"
var value;
addEventListener('GetPatientSearchValues', function() {
var value= 20;
FunctionName();
});
Function FunctionName();
{
value = value + 1;
}
My problem is i need to recieve the value 20 and i need to pass it to FunctioName. But it directly calls the function... even an alert(value) does not work inside the Listener, but the alert works when i don't call another function inside my Listner.
As RobG suggests you could use a global variable (leave out var inside the function), but It would be better to use a parameter:
addEventListener('GetPatientSearchValues', function() {
var value = 20;
value = FunctionName(value);
// use modified value here
});
Function FunctionName(v);
{
v = v + 1;
return v;
}