Will this function successfully make its data available in other contexts? - javascript

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();
});

Related

How to include or detect the name of a new Object when it's created from a Constructor

I have a constructor that include a debug/log code and also a self destruct method
I tried to find info on internet about how to detect the new objects names in the process of creation, but the only recommendation that I found was pass the name as a property.
for example
var counter = {}
counter.a =new TimerFlex({debug: true, timerId:'counter.a'});
I found unnecessary to pass counter.a as a timerId:'counter.a' there should be a native way to detect the name from the Constructor or from the new object instance.
I am looking for something like ObjectProperties('name') that returns counter.a so I don't need to include it manually as a property.
Adding more info
#CertainPerformance What I need is to differentiate different objects running in parallel or nested, so I can see in the console.
counter.a data...
counter.b data...
counter.a data...
counter.c data... etc
also these objects have only a unique name, no reference as counter.a = counter.c
Another feature or TimerFlex is a method to self desruct
this.purgeCount = function(manualId) {
if (!this.timerId && manualId) {
this.timerId = manualId;
this.txtId = manualId;
}
if (this.timerId) {
clearTimeout(this.t);
this.timer_is_on = 0;
setTimeout ( ()=> { console.log(this.txtId + " Destructed" ) },500);
setTimeout ( this.timerId +".__proto__ = null", 1000);
setTimeout ( this.timerId +" = null",1100);
setTimeout ( "delete " + this.timerId, 1200);
} else {
if (this.debug) console.log("timerId is undefined, unable to purge automatically");
}
}
While I don't have a demo yet of this Constructor this is related to my previous question How to have the same Javascript Self Invoking Function Pattern running more that one time in paralel without overwriting values?
Objects don't have names - but constructors!
Javascript objects are memory references when accessed via a variables. The object is created in the memory and any number of variables can point to that address.
Look at the following example
var anObjectReference = new Object();
anObjectReference.name = 'My Object'
var anotherReference = anObjectReference;
console.log(anotherReference.name); //Expected output "My Object"
In this above scenario, it is illogical for the object to return anObjectReference or anotherReference when called the hypothetical method which would return the variable name.
Which one.... really?
In this context, if you want to condition the method execution based on the variable which accesses the object, have an argument passed to indicate the variable (or the scenario) to a method you call.
In JavaScript, you can access an object instance's properties through the same notation as a dictionary. For example: counter['a'].
If your intent is to use counter.a within your new TimerFlex instance, why not just pass counter?
counter.a = new TimerFlex({debug: true, timerId: counter});
// Somewhere within the logic of TimerFlex...
// var a = counter.a;
This is definitely possible but is a bit ugly for obvious reasons. Needless to say, you must try to avoid such code.
However, I think this can have some application in debugging. My solution makes use of the ability to get the line number for a code using Error object and then reading the source file to get the identifier.
let fs = require('fs');
class Foo {
constructor(bar, lineAndFile) {
this.bar = bar;
this.lineAndFile = lineAndFile;
}
toString() {
return `${this.bar} ${this.lineAndFile}`
}
}
let foo = new Foo(5, getLineAndFile());
console.log(foo.toString()); // 5 /Users/XXX/XXX/temp.js:11:22
readIdentifierFromFile(foo.lineAndFile); // let foo
function getErrorObject(){
try { throw Error('') } catch(err) { return err; }
}
function getLineAndFile() {
let err = getErrorObject();
let callerLine = err.stack.split("\n")[4];
let index = callerLine.indexOf("(");
return callerLine.slice(index+1, callerLine.length-1);
}
function readIdentifierFromFile(lineAndFile) {
let file = lineAndFile.split(':')[0];
let line = lineAndFile.split(':')[1];
fs.readFile(file, 'utf-8', (err, data) => {
if (err) throw err;
console.log(data.split('\n')[parseInt(line)-1].split('=')[0].trim());
})
}
If you want to store the variable name with the Object reference, you can read the file synchronously once and then parse it to get the identifier from the required line number whenever required.

Passing a variable from a callback function to another function?

I am working on setting up an HTML5 GeoLocation script and I would like to store the zip code in a cookie but for now I am just trying to figure out how to pass the zip code variable into another function.
Here is my script to reverse geo-code based on lat/long:
function retrieve_zip(callback)
{
try { if(!google) { google = 0; } } catch(err) { google = 0; } // Stupid Exceptions
if(navigator.geolocation) // FireFox/HTML5 GeoLocation
{
navigator.geolocation.getCurrentPosition(function(position)
{
zip_from_latlng(position.coords.latitude,position.coords.longitude,callback);
});
}
else if(google && google.gears) // Google Gears GeoLocation
{
var geloc = google.gears.factory.create('beta.geolocation');
geloc.getPermission();
geloc.getCurrentPosition(function(position)
{
zip_from_latlng(position.latitude,position.longitude,callback);
},function(err){});
}
}
function zip_from_latlng(latitude,longitude,callback)
{
// Setup the Script using Geonames.org's WebService
var script = document.createElement("script");
script.src = "http://ws.geonames.org/findNearbyPostalCodesJSON?lat=" + latitude + "&lng=" + longitude + "&callback=" + callback;
console.log(script.src);
// Run the Script
document.getElementsByTagName("head")[0].appendChild(script);
}
function callback(json)
{
zip = json.postalCodes[0].postalCode;
country = json.postalCodes[0].countryCode;
state = json.postalCodes[0].adminName1;
county = json.postalCodes[0].adminName2;
place = json.postalCodes[0].placeName;
alert(zip);
}
$('#findLocation').click(function(event) {
event.preventDefault();
console.log(zip); // This is giving me undefined currently
});
So basically, in the callback function, I want to store the zip code as a variable(rather than displaying it in an alert) and then in the on click function at the bottom, I want to be able to display the zip code that was stored in the previous callback function.
Any help greatly appreciated, still pretty new to Javscript/jQuery, thanks!
You could set zip as a 'global' variable by including it outside of the function at the top of the document like so:
var zip;
...
Alternatively, you may consider defining an object at the 'global' level and using it as a namespace to store variables like so:
window.address = {};
function callback(json){
address.zip = json.postalCodes[0].postalCode;
address.country = json.postalCodes[0].countryCode;
address.state = json.postalCodes[0].adminName1;
address.county = json.postalCodes[0].adminName2;
address.place = json.postalCodes[0].placeName;
}
$('#findLocation').click(function(event) {
event.preventDefault();
console.log(address.address);
console.log(address.zip);
...
});
I hope this helps!
Define var zip at very begining of code. You haven't defined it.
I haven't tried, but it should solve your problem.
Also, it seems that you forgot to define other variables in callback function as well.
What I would do, is to avoid the anonymous function in the event handler, that is, create a new named function -which gives you the added benefit of traceability during debugging- and then use that function as the event handler callback:
function eventHandlerFunction(event) {
var zip;
event.preventDefault();
zip = eventHandlerFunction.zip;
console.log(zip);
}
function callback(json) {
var zip;
zip = doSomethingWithJsonToGetTheZip();
eventHandlerFunction.zip = zip;
}
$("#findLocation").click(eventHandlerFunction);
Or, better yet, code this as a module and then you have member encapsulation and you can share variables amongst functions without modifying the global object. You never know when another library will modify the same global member that you are using.
var yourModule = (function($) {
var zip;
function retrieveZip(callback) {
// your code
}
function callback(json) {
// do something with json
zip = json.zip; // or whatever
}
$("#findLocation").click(function(event) {
event.preventDefault();
console.log(zip); // zip is visible in the parent scope + this scope
});
}(jQuery));

Creating functions dynamically in JS

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.

use array outside the parent function

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.

How to reference a variable dynamically in javascript

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

Categories

Resources