Alternative to using eval() - javascript

I've heard a lot of rumblings about how "evil" or even "misunderstood" the eval function is, so I've decided to remove it from my code. The problem is I don't know what to replace it with.
Here's a quick rundown of my current code. I have a series of arrays (just 2 for the example below) declared at the beginning, and then based on a button click one of them gets loaded into a variable that is passed into a function.
Here's some basic HTML
<div class="button" data-name="button1">Button1</div>
<div class="button" data-name="button2">Button2</div>
and the JS (with jQuery)
var butName = null;
var eArray = null;
var button1Logo = ["..path/to/pic1.png","..path/to/pic2.png"];
var button2Logo = ["..path/to/pic3.png","..path/to/pic4.png"];
$(".button").mouseup(function(){
/*give a butName*/
butName = $(this).attr("data-name");
/*give the array from the button*/
eArray = eval(butName + "Logo");
});
Doing it this way assigns the array to the variable and not just a string that says "butnameLogo" which is why I used eval. But I'm looking to get away from that.
I know I can add a new attribute to the html and just retrieve that for the variable but I don't want to add more html when I can possibly do it with JS.
I've also tried making an object with strings loaded into it as seen in this answer: https://stackoverflow.com/a/16038097/1621380 but that resulted in just a string again, and not assigning a variable.
Wondering if you smart people have any better suggestions!

Replace
var button1Logo = ["..path/to/pic1.png","..path/to/pic2.png"];
var button2Logo = ["..path/to/pic3.png","..path/to/pic4.png"];
with an object, where the keys are your button names:
var buttonLogos = {
button1: ["..path/to/pic1.png","..path/to/pic2.png"],
button2: ["..path/to/pic3.png","..path/to/pic4.png"]
};
Then instead of the eval you can simply do
eArray = buttonLogos[butName]
(or buttonLogos[butName + "Logo"] if you want to call the keys button1Logo and button2Logo, but I can't really see the point now that they are nicely contained within a buttonLogos object)

Use an object:
var butName = null;
var buttonsLogos = {
button1: ["..path/to/pic1.png", "..path/to/pic2.png"],
button2: ["..path/to/pic3.png", "..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/*give a butName*/
butName = $(this).attr("data-name");
/*give the array from the button*/
eArray = buttonsLogos[butName];
});

Consider making the data available as properties of an object, then you can control access to the object through scope and only need one (global?) variable for all such data.
If global scope is needed, then:
var dataObj = {
button1Logo: ["..path/to/pic1.png","..path/to/pic2.png"],
button2Logo: ["..path/to/pic3.png","..path/to/pic4.png"]
}
and later:
var eArray = dataObj[this.data-name + 'Logo'];
You may want to call the data object something more meaningful than dataObj though.

The best option is to define an object which holds all our button paths:
var buttons = {
"1": ["..path/to/pic1.png", "..path/to/pic2.png"],
"2": ["..path/to/pic3.png", "..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/* give a butName */
var butName = $(this).attr("data-name");
/* give the array from the button */
var eArray = buttons[butName];
});
If your variables reside in the global scope, you could use the bracket notation to access them:
eArray = window[butName + "Logo"];
Note that this solution is not recommended. The first code sample is much cleaner and more maintainable.
Imagine a situation where you would have to move all the code into a 'deeper' context (!= global context). Nothing would work anymore.

You can do this very nicely with arrays and array indexes. You needn't find and use variable names at all. Even your data- attributes are unnecessary.
var eArray;
var buttonLogos = [
["..path/to/pic1.png","..path/to/pic2.png"],
["..path/to/pic3.png","..path/to/pic4.png"]
];
var buttons = $(".button").mouseup(function(){
var idx = buttons.index(this);
eArray = buttonLogos[idx];
});
The key line in this is buttons.index(this). This method call gets the position of the current element among all the elements matched by $(".button"). We then use this index to select the relevant element from the buttonLogos array.

You're taking a very circuitous route by using eval here.
You'd be much better off doing something like this:
var paths = {
button1: ["..path/to/pic1.png","..path/to/pic2.png"],
button2: ["..path/to/pic3.png","..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/*give the array from the button*/
eArray = paths[$(this).attr("data-name")];
});
eval should only be used if you need to execute code (usually from a 3rd party source), and even that is rare. If you ever find yourself saying "i should use eval here", there's almost definitely a better alternative, and you should try and find it.

Related

What methods or procedures need to be in place to append in jQuery a simple Java 'class' instance that represents a jQuery implementation?

I have this class in Javascript: call it Caption:
function Caption
{
var ...
function get...()
{ }
function set...(...)
{ ... }
return( { get...: get...
, set...: set...
});
}
This is only one component of a larger system and is part of an outer class called Filter. I would love to be able to when creating the jQuery for the object to be able to say:
tblFilter.append(getCaption())
which would get the Caption class instance variable and append the jQuery representation of it. Do I need to inherit from jQuery to make that happen? Like this?
function Caption
{
var ...
function get...()
{ }
function set...(...)
{ ... }
var that = jQuery();
that.get... = get...;
that.set... = set...;
return(that);
}
?
If that's true, what I'm not sure of is what function/method I need to write to produce the jQuery that is produced to be appended to the outer jQuery. Does this make sense and if so, what am I missing?
Edit:
Let me elaborate - Ok I'm creating my own version of a Data Table. Yes I know jQueryUI has but with this, I pass the data and it renders the data. There's a Filter component that really can't be created/constructed publicly but just accessed by a getFilter() method. Every time the Filter instance is changed, thanks to a home grown listener/observer/observable pattern, the filter is erased and re-rendered. As of now much of it is hard coded. When I first wrote it it was more loosely written as it was for a programming assignment to get a job and I did it within 2 days I had. Now that it's done I'm trying to implement it more generically into a library. When I get to appending the Caption and trying to make it render it based on the Caption object, I have no ID for a Caption or a parent object to remove. Ideally I'd love to be able to do:
var objDataTable = new DataTable(/*parameters*/);
$(/*parent selector/*).append(objDataTable);
I'm self taught so my Object Oriented Programming is a bit all over the place, but if I had a class, that I wanted to 'HTMLize' and insert values from into the dom, I would do it like this:
var Caption = {
'amount':10,
'word':'Hello!',
'getCaption': function(appendToObject){
var div = $('<div />');
var ul = $('<ul />');
var li = $('<li />', {
'text' : this.word
});
li.appendTo(ul);
var li2 = $('<li />', {
'text' : this.amount
});
li2.appendTo(ul);
ul.appendTo(div);
div.appendTo(appendToObject);
}
};
var caption_instance = Caption;
caption_instance.getCaption('#wrapper');
var second_caption = Caption;
second_caption.amount = 13;
second_caption.word = 'Goodbye';
caption_instance.getCaption('#wrapper');
You can see it in action here:
http://codepen.io/EightArmsHQ/pen/bVrapW
If you are including jQuery globally you don't need to pass anything to the function, you can just get going.
The important part here is the var xxx = $('<DOM_TAG />'); which lets you create elements to append to the DOM. This can be found here: http://api.jquery.com/jquery/#jQuery2
If I've misunderstood the question just let me know and I'll remove the answer.

using a string to create a variable

I'm trying to create variables using strings.
I'm writing a script that will load images from a array of strings and I want to store those images in variables with the name of the used string.
This is the code so far:
var images = ["CrazyWolf.jpg", "CrazyWolf.jpg", "CrazyWolf.jpg", "CrazyWolf.jpg"];
var name = "";
function preload(path)
{
img = new Image();
img.src = path;
img.onload = imageLoaded;
return img;
};
function makeImages()
{
for(var i = images.length; i > 0; i--)
{
source = images.shift();
preload(source);
var name = source.replace(".jpg", "Image");
console.log(name);
//make vars with as name, var name
//Console.log(name); returns: CrazyWolfImage
if(i==0) console.log("Loop ended");
}
}
How do I use this name to create variables out of it?
Any help would be greatly apreciated!
Thanks!
What you ask for is quite easy. You can store the image as a property in the window object, and it can be accessed as a global variable:
function makeImages() {
while (images.length) {
source = images.shift();
var name = source.replace(".jpg", "Image");
window[name] = preload(source);
}
}
However, creating variables dynamically isn't very useful, and it can have unforseen side effects. The code to use the variables either has to blindy use some variables depending on them being created, or access them dynamically which makes it pointless to have them as variables in the first place. The global namespace is already full of identifiers, so there is a risk that you use a name that already exists.
Instead of using the global namespace, create an object where you store the images by name. That way they are isolated from the global namespace and its existing identifiers. You don't even have to manipulate the name to make a valid identifier, a name like CrazyWolf.jpg works just fine to access properties:
var img = {};
function makeImages() {
while (images.length) {
source = images.shift();
img[source] = preload(source);
}
}
Now you can access the images dynamically from the object. Example:
document.getElementById('#ShowImage').src = img['CrazyWolf.jpg'].src;
You can also loop through the images in the object, which would not be possible if they were variables among all the existing in the global namespace:
for (image in img) {
image.style.width = '200px';
}
Note: When looping through the properties in an object you can't rely on the order that they are processed. You would use an array instead of an object if you want to retain a specific order.
you can say something like this:-
eval("var number = 5");
console.log(number);
It'll print 5 as answer.
I think it will be helpful.
You can't create non-global variables with constructed names without using eval() (which would be a pretty bad idea), but you can create object properties:
var imgs = {};
for(var i = images.length; i > 0; i--)
{
source = images.shift();
preload(source);
var name = source.replace(".jpg", "Image");
console.log(name);
imgs[name] = whatever;
}
eval() should be avoided because it makes internal code optimization impossible, if not for oft-discussed aesthetic reasons.
Global variables are properties of the global context (window) so if you want global variables (why?) you can use that instead of a local object.

How can I use multiple variables to retrieve JS objects?

I'm only working on my 3rd Javascript project, so this is probably easy to answer (at least I hope so).
I have learned to use JS object in place of arrays. In this project I have named multiple object with a nested system of IDs as follows:
animalia = new Object();
animalia.chordata = new Object();
animalia.chordata.actinopterygii = new Object();
animalia.chordata.actinopterygii.acipenseriformes = new Object();
etc.......
I'm having problems calling on objects named this way though. Here is my code:
function expand(event){
var target = event.target;
console.log(target);
var parent = target.parentNode;
console.log(parent);
var parentclass = parent.getAttribute("class");
console.log(parentclass);
if (parentclass == "kingdom"){
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentid = parent.getAttribute("id");
console.log(parentid);
----> var parentobj = window[parentid];
console.log(parentobj);}
else{
var upperclass = searchArray(parentclass);
console.log(upperclass);
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentId = parent.getAttribute("id");
console.log(parentId);
var parentnode_ = document.getElementById(parentId);
console.log(parentnode_);
var gparentId = parentnode_.parentNode.id;
console.log(gparentId);
----> var parentobj = window[gparentId.parentId];
console.log(parentobj);
}
var childnumb = parentobj.children;
}
I am having my problem with the two statements indicated by "---->". In the first case, using a single variable works for pulling up the proper object. However, in the second case, using two variables, I fail to be able to access the proper object. What is the proper syntax for doing this? I have tried a plethora of different syntax combinations, but nothing seems to work correctly. Or should is there a better method for calling on JS objects other than using "window[variable]"?
P.S.- If you haven't figured it out by now, I am working on educational tools for use in learning biology. Thanks once again stackoverflow, you guys rule.
Assuming that the window object has something w/ the property matching a string that's the value of gparentId, you should be able to do:
var parentobj = window[gparentId][parentId];
The problem here is that the square bracket's notation is being applied to too much. gparentId is a string. It doesn't have a property called parentId. You therefore have to do this in two steps. First get:
window[gparentId]
Then get the appropriate property of that object
var parentobj = window[gparentId][parentId];
On a somewhat unrelated note, this isn't very well written JavaScript code:
Creating Objects
When creating new objects, always use the following syntax:
var obj = {};
That's what's generally been accepted as standard, so it's easier for people to read.
Declaring Variables in If Statements
You shouldn't really declare variables inside an if statement, especially when declaring the same variable in the else block, that's really confusing. Instead, declare all the variables at the top in a list and then use them without the var keyword lower down.
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj;
Note the commas instead of semi-colons which means I don't have to repeat the var keyword. Since the values of newdiv, newexpctrl and parentid are the same in either case, I give them their values straight away, making the contents of the if statement much shorter and easier to digest.
Result
function expand(event){
var target = event.target;
var parent = target.parentNode;
var parentclass = parent.getAttribute("class");
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj, upperclass;
if (parentclass == "kingdom"){
parentobj = window[parentid];
}else{
upperclass = searchArray(parentclass);
var _parentId = document.getElementById(parentId).parentNode.id;
parentobj = window[_parentId][parentId];
}
var childnumb = parentobj.children;
}
Note that I've left var _parentId inside the if since I think it probably improves readability, but you may choose to take it outside the if, since it will pollute the namespace of the function anyway.

Object within object add property with JavaScript

I have an object within an object. It looks like this.
var myLib = {
object1: {}
}
My basic problem is that I wanted to end up like this. So I would like to do this dynamically I will not know the property's or additional objects until run time.
var myLib = {
object1: ({"A1":({"Color":"Blue",
"height":50})
})
}
From reading here on Stack Overflow I know that I can create an object within an object by simply going like this:
myLib.Object1["A1"] = "Something"
But this does not produce what I'm looking for.
I tried this syntax which I know is wrong but basically
mylib.Object1["A1"].["color"]="Blue";
so basically here is the question. I would like to create object "A1" under "mylib.Object" and immediately add property color = "blue" to "A1". I would need to do this for several other properties, but if I can figure out how to do this for one, I can figure it out for the rest. How can I accomplish this task?
No jQuery, please. Just plain old JavaScript is what I'm looking for.**
Once I create the object and properties I would imagine I can just use a for loop to loop through the properties for that object. Like so:
for(key in mylib.Object1["A1"]){}
Right?
You can create it all from scratch like this:
var myLib = {};
myLib.object1 = {};
// assuming you get this value from your code somewhere
var x = "A1";
myLib.object1[x] = {Color: "Blue", height: 50};
Or, if all values are in variables:
var myLib = {};
myLib.object1 = {};
// assuming you get this value from your code somewhere
var x = "A1";
var colorProp = "Color";
var colorPropValue = "Blue";
var heightProp = "height";
var heightPropValue = 50;
myLib.object1[x] = {}; // create empty object so we can then add properties to it
myLib.object1[x][colorProp] = colorPropValue; // add one property
myLib.object1[x][heightProp] = heightPropValue; // add another property
These syntaxes create identical results:
myLib.object1.A1 = {};
var x = "A1";
myLib.object1[x] = {};
The first can only be used when the property name is known when you write the code and when the property name follows the proper rules for a javascript identifier. The second can be used any time, but is typically used when the property name is in a variable or when it doesn't follow the rules for a javascript identifier (like it starts with a digit).

Global var in JavaScript

This is annoying me.
I'm setting an array in beginning of the doc:
var idPartner;
var myar = new Array();
myar[0] = "http://example.com/"+idPartner;
And I'm getting a number over the address, which is the id of partner. Great. But I'm trying to set it without success:
$.address.change(function(event) {
idPartner = 3;
alert(idPartner);
}
Ok. The alert is giving me the right number, but isn't setting it.
What's wrong?
Changing the value of the variable does not re-set the values within the array. That is just something javascript can't do automatically. You would have to re-generate the array for it to have the new id. Could you add the id to the value where you use the array instead of pre-setting the values in the array containing the id?
Edit: For example, you would do:
var myArray = [];
var myId = 0;
myArray[0] = "http://foo.com/id/";
and when you need to use a value from the array, you would do this:
var theVal = myArray[0] + myId;
Try this:
var myvar = ["http://site.com/"];
$.address.change(function(event) {
myvar[1] = 3;
}
then use myvar.join () where you need the full url.
The problem here is that at the line
myar[0] = "http://site.com/"+idPartner;
..you perform a string concatenation, meaning you copy the resulting string into the array at index position 0.
Hence, when later setting idPartnerit won't have any effect on the previously copied string. To avoid such effect you can either always construct the string again when the idPartnervariable updates or you create an object and you evaluate it when you need it like...
var MyObject = function(){
this.idPartner = 0; //default value
};
MyObject.prototype.getUrl = function(){
return "http://site.com/" + this.idPartner;
};
In this way you could use it like
var myGlblUrlObj = new MyObject();
$.address.change(function(event){
myGlblUrlObj.idPartner = ... /setting it here
});
at some later point you can then always get the correct url using
myGlblUrlObj.getUrl();
Now obviously it depends on the complexity of your situation. Maybe the suggested array solution might work as well, although I prefer having it encapsulated somewhere in an object for better reusability.
myar[0] = "http://site.com/" + idPartner;
After this line, myar[0] = "http://site.com/undefined" and it has nothing to do with the variable idPartner no more.
So, after that changing the value of idPartner will affect the value of myar[0].
You need to change the value of myar[0] itself.

Categories