with this function triggered as a callback
var testing = [];
var vid;
function showMyVideos(data){
var feed = data.feed; //object "feed"
var entries = feed.entry || []; //array "entry"
var html = ['<ul>'];
for (var i = 0; i < entries.length; i++){
entry = entries[i];
playCount = entry.yt$statistics.viewCount.valueOf() + ' views';
title = entry.title.$t;
vid = (getVideoId(entry.link[0].href));
testing[i] = vid;
lnk = '<a href = \'' + entry.link[0].href + '\'>link</a>';
html.push('<li>', title, ', ', playCount, ', ', vid, ', ', lnk, '</li>');
}
html.push('</ul>');
$('#videoResultsDiv').html(html.join(''));
}
I want to use the "testing" array on other functions, how will I do that?.. I'm frustrated now, sorry I'm just starting to appreciate JavaScript. I want to perfectly access the array data like when I'm doing on
console.log(testing)
inside the function..
"testing" is in the global scope and it should be accesible anywhere as long as there isn't another variable in another scope with the same name overriding the global "testing".
If the values aren't accesible in the place you are trying it, it could be because that function is getting executed before the callback loads the values in "testing".
As has been mentioned testing is global scope, so you can use it anywhere. So it's important that you have the correct execution order for the functions using testing.
Some might frown on using global scope in javascript but there are ways to mitigate issues.
Generally when I have to implement global scope vars, I will create a method along the lines of 'clear_session' or 'reset_globals'.
In your case a function like that would look like this:
function reset_globals(){
testing = [];
vid;
}
This is useful because then you know that after you call reset_globals() that your variables are going to be empty.
Related
In this code excerpt, I'm trying to figure out whether the data variable from within the setExtension function will be available within the Object.keys context. Since the setExtension function is meant to change data.layout when extension is available.
function setExtension(file) {
var data = files[file];
if (extension) data.layout = data.layout + '.' + extension;
}
Object.keys(files).forEach(function(file){
if (!check(file)) return;
setExtension(file);
debug('stringifying file: %s', file);
var data = files[file];
data.contents = data.contents.toString();
});
I would say setExtension does nothing because it creates data in its scope and it doesn't return data. But I'm having trouble figuring out whether I'm actually correct, or overlooking something trivial.
The data variable won't be available outside of it's local function scope, because it was declared with the var keyword and Javascript has function level scoping.
If you remove the var keyword, then it would be available, because it would be declared on the global object. However, this is a bad idea, so don't do it.
What you can do instead is return the data variable from the setExtension function.
function setExtension(file) {
var data = files[file];
if (extension) data.layout = data.layout + '.' + extension;
return data;
}
Then you can get hold of the data variable by changing your forEach:
Object.keys(files).forEach(function(file){
if (!check(file)) return;
var data = setExtension(file);
debug('stringifying file: %s', file);
data.contents = data.contents.toString();
});
I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.
Here's the question:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.
But when I ran the below:
countFunctions[0];
countFunctions[1];
countFunctions[2];
It didn't work, with all the output being stuck at count2.
So I tried to do the below instead:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.
Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.
The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").
What you didn't notice is that setInner does, when called, return the closure function:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.
Take a look at these four functions:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
I think, first function is obvious.
In function noArguments you reference the global argument value;
The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.
So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments,
that reference global variable i.
In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.
In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.
I am creating the AI engine for a JS game, and it's made of Finite State Machines. I am loading the number of states and their variable values from the XML. I also want to load the behaviour, and since I don't have the time to create a scripting language, I thought it would be a good idea to 'insert' JS code on external files (inside XML nodes), and execute it on demand.
Something like that
<evilguy1>
<behaviour>
this.x++;
</behaviour>
<behaviour>
this.y++;
</behaviour>
</evilguy1>
To something like that:
function behaviour_1(){
this.x++;
}
function behaviour_2(){
this.y++;
}
My question is, now that I have the code loaded, how can I execute it? I would like to create a function with an unique name for each code 'node', and then call them from the game logic, but I don't know if this is possible (Since you can load more JS code from the HTML, you should also be able to do it from the JS code, no?). If not, is there any similar solution? Thanks in advance!
(PS:The less external-library-dependent, the better)
Edit 1:
Ok, so now I know how to create functions to contain the code
window[classname] = function() { ... };
Well, you could use Function constructor, like in this example:
var f = new Function('name', 'return alert("hello, " + name + "!");');
f('erick');
This way you're defining a new function with arguments and body and assigning it to a variable f. You could use a hashset and store many functions:
var fs = [];
fs['f1'] = new Function('name', 'return alert("hello, " + name + "!");');
fs['f1']('erick');
Loading xml depends if it is running on browser or server.
To extend Ericks answer about the Function constructor.
The Function constructor creates an anonymous function, which on runtime error would print out anonymous for each function (created using Function) in the call stack. Which could make debugging harder.
By using a utility function you can dynamically name your created functions and bypass that dilemma. This example also merges all the bodies of each function inside the functions array into one before returning everything as one named function.
const _createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.splice(1, 0, '\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
A heads up about the Function constructor:
A function defined by a function expression inherits the current
scope. That is, the function forms a closure. On the other hand, a
function defined by a Function constructor does not inherit any scope
other than the global scope (which all functions inherit).
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Differences
Assuming you have an array of node names and a parallel array of function body's:
var functions = {};
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviorsNames.length; i++){
functions[behaviorsNames[i]] = new Function(behaviorsBodies[i]);
}
//run a function
functions.behavior1();
or as globals:
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviors.length; i++){
window[behaviors[i]] = new Function(behaviorsBodies[i]);
}
All of the above answers use the new Function() approach which is not recommended as it effects your app performance. You should totally avoid this approach and use window[classname] = function() { ... }; as #user3018855 mention in his question.
I have some code like this:
for(var id=0; id < message.receiver.length; id++){
var tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
}
And I got a warning that using tmp_id in closure may cause problem because it is a mutable variable.
How could I avoid that? I mean how could I send an immutable variable to callback since this is a for loop and I can not change code of zlib.gzip? Or in other words, how could I pass a argument to a closure?
You need to create a scope to correctly capture tmp_id using a self-executing function. That's because the entire for loop is one scope, meaning each time through, you're capturing the same variable. So the callback will end up with the wrong ids, because temp_id's value will get changed before the callback is called.
I'd ignore (or shut off) the warning, though, which seems to be complaining that because temp_id is mutable, you might reassign it. That's sort of silly. If you really want to fix it, try using the const keyword instead of var.
for(var id=0; id < message.receiver.length; id++){
(function(){
const tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
})();
}
I have faced the same problem and solved it slightly modifying the answer of user24359, by passing the id to the closure:
for(var id=0; id < message.receiver.length; id++){
(function(tmp_id){
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
})(id);
}
here a simplification of user24359's great answer.
This is the solution:
var object = {a:1,b:2};
for (var y in object){
(function(){const yyy = y;
setTimeout(function(){console.log(yyy)},3000);})();
}
The above code logs a b and is the solution.
The following code logs b b :
var object = {a:1,b:2};
for (var y in object){
setTimeout(function(){console.log(y)},3000);
}
I've faced the same problem in protractor. Solved it using following code -
(function(no_of_agents){
ptor.element.all(by.repeater('agent in agents').column('displayName')).then(function(firstColumn){
console.log(i, '>>>>>Verifying the agent Name');
var agentsSorted = sortAgentsByName();
//verify the agent name
expect(firstColumn[no_of_agents].getText()).toEqual(agentsSorted[no_of_agents].name);
//now click on the agent name link
firstColumn[no_of_agents].click();
ptor.sleep(5000);
});
})(no_of_agents);
#user24359 answer is a good solution but you can simply replace the var keyword by the let keyword.
for(var id=0;
becomes
for(let id=0;
See details here.
Edit : As Heriberto Juárez suggested it, it will only works for browsers that supports EcmaScript6.
Creating closures in a loop with var (tmp_id) being in the upper scope of the callback function is a common mistake that should be avoided due to the var not being block-scoped. Because of this, and because each closure, created in the loop, shares the same lexical environment, the variable will always be the last iterated value (i.e. message.receiver.length - 1 as tmp_id) when the callback function gets invoked. Your IDE detects this behavior and complains rightly.
To avoid the warning, there are several solutions:
Replace var with let ensuring each created closure to have its own scoped tmp_id defined in each iteration:
for (var id = 0; id < message.receiver.length; id++) {
let tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer) {
// Do something with tmp_id ...
});
}
Create a lexical environment in each iteration by leveraging IIFE like gennadi.w did.
Create a callback function in each iteration by using a factory function (createCallback):
const createCallback = tmp_id => function(err, buffer) {
// Do something with tmp_id ...
};
for (var id = 0; id < message.receiver.length; id++) {
zlib.gzip(JSON.stringify(message.json), createCallback(id));
}
bind the variable(s) on the callback function in which they get prepended to its parameters:
for (var id = 0; id < message.receiver.length; id++) {
zlib.gzip(JSON.stringify(message.json), function(tmp_id, err, buffer) {
// Do something with tmp_id (passed as id) ...
}.bind(this, id));
}
If possible, var should be avoided as of ECMAScript 2015 due to such error-prone behaviors.
I am trying to reference a variable dynamically in javascript
The variable I am trying to call is amtgc1# (where # varies from 1-7)
I am using a while statement to loop through, and the value of the counting variable in my while statement corresponds with the last digit of the variable I am trying to call.
For Example:
var inc=3;
var step=0;
while(step < inc){
var dataString = dataString + amtgc1#;
var step = step+1;
}
Where # is based on the value of the variable "step". How do I go about doing this? Any help is appreciated! Thanks!!
Rather than defining amtgc1[1-7] as 7 different variables, instantiate them as an array instead. So your server code would emit:
var amtgc1 = [<what used to be amtgc11>,<what used to be amtgc12>, ...insert the rest here...];
Then, you can refer to them in your loop using array syntax:
var dataString = dataString + amtgc1[step];
The only way you can do this (afaik) is to throw all of your amtgc1# vars in an object such as:
myVars = {
amtgc1: 1234,
amtgc2: 12345,
amtgc3: 123456,
amtgc4: 1234567
};
Then you can reference it like
myVars["amtgc" + step];
How about:
var dataString = dataString + eval('amtgc1' + step);
It is true that eval() is not always recommended, but that would work. Otherwise depending on the scope, you can reference most things like an object in JavaScript. That said, here are examples of what you can do.
Global scope
var MyGlobalVar = 'secret message';
var dynamicVarName = 'MyGlobalVar';
console.log(window.[dynamicVarName]);
Function Scope
function x() {
this.df = 'secret';
console.log(this['df']);
}
x();
Not tested, but can't see why you can't do this...
$('#amtgc1' + step).whatever();
If your amtgc1* variables are defined as a property of an object, you can reference them by name. Assuming they are declared in the global scope, they will be members of the window object.
var inc=7;
var step=0;
while(step < inc){
var dataString = dataString + window['amtgc1'+(step+1)];
var step = step+1;
}
If they are defined in a different scope (within a function) but not belonging to any other object, you're stuck with eval, which is generally considered bad.
also, hooray for loops!
var inc=7;
for ( var step=0; step < inc; step++ ){
var dataString = dataString + window['amtgc1'+(step+1)];
}
I have built a way which you could solve this problem using objects to store the key values, where the key would be the reference to the task and the value will be the action (function) and you could use an if inside the loop to check the current task and trigger actions.
If you would like to compare dynamically concatenating strings with "variable", you should use the eval() function.
/* store all tasks references in a key value, where key will be
* the task reference and value will be action that the task will
* Execute
*/
var storeAllTasksRefer = {
amtgc11:function(){ alert("executing task amtgc11"); },
amtgc112:function(){ alert("executing task amtgc112"); },
"amtgc1123":"amtgc1123"
// add more tasks here...
};
var inc = 7;
var step = 1;
var dataString = 'amtgc1';
while(step <= inc){
var dataString = dataString + step;
//alert(dataString); // check its name;
step = step+1;
// check if it is my var
if( dataString == 'amtgc112' ){
// here I will reference my task
storeAllTasksRefer.amtgc112();
}// end if
/* you can also compare dynamically using the eval() function */
if('amtgc1123' == eval('storeAllTasksRefer.'+dataString)){
alert("This is my task: "+ eval('storeAllTasksRefer.'+dataString));
} // end this if
} // end while
Here is the live example: http://jsfiddle.net/danhdds/e757v8ph/
eval() function reference: http://www.w3schools.com/jsref/jsref_eval.asp