Should I use eval inside javascript? - javascript

My issue is that I have a very long string to parse (basically an object represented as a string), I'm trying to parse it manually without using eval, I've got more than 1000 lines of looping functions and I'm not even close to complete the algorithm.
I was checking how this was done in jQuery.metadata and they just used eval! my 1000 lines of code can be shrink to just an eval, but is this safe? I've heard that this function is not safe neither fast, but my algorithm is also slow considering all those loops and parsing.
e.g.
<button onajax="{reload:'#someitem',callback: function('somedata'),items:{1,2,3}}">
I need to set
var onajaxargs = {reload:'#someitem',callback: function('somedata'),items:{1,2,3}};

If you can give a little bit more information on what you are trying to do then I can better answer your question, but like Roman said, JSON seems to be the best method for Parsing the string.
This explains how to use JSON.parse() pretty well:
Parse JSON in JavaScript?

See Why is using the JavaScript eval function a bad idea? if you have not already.
I would suggest using this approach of first encoding to JSON, and then "reviving" your function members.
http://ovaraksin.blogspot.com/2013/10/pass-javascript-function-via-json.html
jsonText='{"reload":"#someitem","callback": "function(somedata) {alert(somedata)}","items":[1,2,3]}';
var jsonTransformed = JSON.parse(jsonText, function (key, value) {
if (value && (typeof value === 'string') && value.indexOf("function") === 0) {
// we can only pass a function as string in JSON ==> doing a real function
var jsFunc = new Function('return ' + value)();
return jsFunc;
}
return value;
});
Several observations:
You need to have a canonical JSON to make sure it works - all strings and names of members to be enclosed in double quotes.
You would need to provide proper escaping of the quotes (single and double) in the string.
You need to make the function members to contain the text "function" in the beginning of the string.
Array members to be enclosed with square brackets, not with curly braces as in your original post.

Related

When do I need to create a new variable within a function?

I'm a new learner of JavaScript, and when I get to learn the way of using a function. It sometime confuses me on why we should declare a new variable and add the variable to the action we want to execute. Let's look into the code.
function reverse(word){
Array.from(word);
let reverseWord='';
for(i = word.length-1; i >= 0; i--) {
reverseWord += word[i];
}
return reverseWord;
}
I'm sure you know this one of the way of reversing string in javascript, my question is:
Why do we need to declare a new variable within the function, when should we declare it?
Why can't I just type console.log(word[i]);?
What does it mean by wordLength+=word[i];?
Why should we return the new variable(wordLength), instead of the function(reverse) after the loop?
Why do we need to declare a new variable within the function...
Because you need a place to store the reversed word as you build it. (Note: wordLength isn't a good name for that variable. It doesn't contain the word's length. It contains the characters of the reversed word.)
...when should we declare it?
Any time before you first need it.
Why can't I just type console.log(word[i]);?
Because the goal of the exercise is to build a string containing the reversed word, not just to output it. (And because console.log writes a new line each time you call it.)
What does it mean by wordLength+=word[i];?
That adds the character in word[i] to the end of wordLength. For instance, if the word is "start", wordLength starts out with "", then gets "t" added to it to make it "t", then gets "r" added to it to make "tr", and so on.
(+= is a shorthand way to write wordLength = wordLength + word[i];. There are several of these compound assignment operators, most of them for math: -=, *=, etc.)
Side note: The Array.from call in your code isn't doing anything useful. It's creating an array, but then throwing that array away because nothing uses the return value. The rest of the code is using the string you receive in word.
Why do we need to declare a new variable within the function, when should we declare it?
Vars is a place to store data. If your algorithm requires keeping some data to use it later you need vars. Also well named variables is a good way to create easy-to-understand code
Why can't I just type console.log(word[i]);?
You can, but it will do nothing useful. Your goal is to build a string and return it. Usage of your function will be something like
const word = getSomeText()
const reversedText = reverse(word)
doSomeStuff(reversedText) // whatever, send it online, or render it on screen some fancy way, not in the console.
So you need to return actual string, not to solve a puzzle and show the answer whatever way you like
Why should we return the new variable(wordLength), instead of the function(reverse) after the loop?
Because it contains reversed word and you function supposed to return it. there is rare complicated occasions when a function returning itself is useful, but it has nothing in common with your task
Why do we need to declare a new variable within the function, when should we declare it?
Variable is required to store the data value that can be changed later on. In your case wordLength variable is required to store the reverse string.
It's best to declare variables when you first use them to ensure that they are always initialized to some valid value.
Why can't I just type console.log(word[i])
console.log() is used just to print the output but will not use if you want to return something and as per your statement it will just print the word[i] not a whole reverse string.
What does it mean by wordLength+=word[i]
It means you are concatenating the each iteration word[i] into a wordLength variable.
wordLength+=word[i] is a shorthand for wordLength = wordLength + word[i]. If the left hand side of the + operator is a string, JavaScript will coerce the right hand side to a string.
Why should we return the new variable(wordLength), instead of the function(reverse) after the loop ?
Because this is what you expected from the function. It returns the reversed string and function should return it.

What does .join`` mean in JavaScript? [duplicate]

This question already has answers here:
Backticks (`…`) calling a function in JavaScript
(3 answers)
Closed 2 years ago.
I came across this code:
new Array(10).fill('1').join``;
I do not know what the meaning of the signs `` used immediately after .join is.
I thought the correct syntax would be new Array(10).fill('1').join('').
Any ideas are welcome thanks!
const data = new Array(10).fill('1').join``;
console.log(data)
What you're seeing is a tagged template literal, part of the ES6 specification. That pattern is heavily used in e.g. Google's Polymer Project (now lit-element). It invokes the supplied function (or in this case method) with the supplied template literal.
In addition to being used to create a Domain-Specific Language (DSL) of sorts, it's also frequently used to reduce the byte count for Javascript answers in code golf In the case of the Google project I linked, it's used as part of a domain-specific HTML templating language.
arr.join``; is a tagged template literal.
It applies join (or any other function) to "the content of the template literal string".
When I say "applies to the content of the template literal string" it is actually a little bit more complicated than that: all static parts of the string are put into an array as the first argument and the interpolated values as the remaining arguments.
A simple example will make it easier to understand:
var demo = (str, ...names) => {
console.log(str);
console.log(names);
};
var users = ['john', 'jane', 'joe'];
demo`Members: ${users[0]}, ${users[1]} and ${users[2]}.`;
// LOG: ["Members: ", ", ", " and ", "."]
// LOG: ["john", "jane", "joe"]
demo``;
// LOG: [""]
// LOG: []
The last line answers your question because arr.join``; is the same as arr.join(""); just two characters shorter which is useful when you compete in JS1K for example.
In this particular case I'm not sure that the author wanted to save a few bytes because we can shorten the initial statement as follow:
new Array(10).fill('1').join``;
new Array(10).fill(1).join``;
Array(10).fill(1).join``;
'1'.repeat(10);
'1111111111';
Is a tagged template just a glorified function call?
These two expressions may look similar but they are not:
var yy = 20;
year`19${yy}`;
year(`19${yy}`);
In the first expression we have access to the static and dynamic parts of the string: "19" and 20. In the second expression we only get the "final" string "1920".
This is useful when one wants to preserve intent yet allow complex text processing behind the scene.
We know that this is unsafe:
var sql = `SELECT * FROM users where email="${email}" AND password="${password}"`;
To mitigate the risk of SQL injection, it is not uncommon to see things like:
var sql = select('*').from('users').where('AND').eq('email', email).eq('password', password);
However it could be argued that we have traded the expressiveness of SQL with a somewhat awkward API that people are less likely to be familiar with.
If a tagged template literal has access to both static and dynamic parts of a string we can build domain-specific tagged template literals:
var sql = safe_sql`SELECT * FROM users where email="${email}" AND password="${password}"`;
This syntax is called Tagged Template literals , Calling a function by passing backitlists.
It works that way
function taggedTemplate(str, ...exps){
//str is an [] containg the strings in the tagged literals.
//exps is an [] containg the expressions passes in the tagged literals
console.log(str[0]) // "Number "
console.log(exps[0]) // "19"
}
taggedTemplate`Number ${19}`
So, The join method supports tagged literals, It could be something like this.
Array.prototype.join = function(arg){
let str = typeof arg === "string" ? arg : arg[0];
//joins the array using str
}
It is an abomination that works. So you are correct in assuming that when you do a join function call, you pass a empty string so that there is nothing put in between the resulting string.
In JavaScript, you can declare strings with "quotes", 'apostrophes', and `template literals`.
The developer who wrote that line of code thought they were being clever by skipping the opening and closing parentheses, when in reality they just made the code slower, more complex, and more confusing for everyone else.

Regex to define the number of appearances substituted [duplicate]

I'd like to know how to replace a capture group with its uppercase in JavaScript. Here's a simplified version of what I've tried so far that's not working:
> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
Would you explain what's wrong with this code?
You can pass a function to replace.
var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Explanation
a.replace( /(f)/, "$1".toUpperCase())
In this example you pass a string to the replace function. Since you are using the special replace syntax ($N grabs the Nth capture) you are simply giving the same value. The toUpperCase is actually deceiving because you are only making the replace string upper case (Which is somewhat pointless because the $ and one 1 characters have no upper case so the return value will still be "$1").
a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Believe it or not the semantics of this expression are exactly the same.
I know I'm late to the party but here is a shorter method that is more along the lines of your initial attempts.
a.replace('f', String.call.bind(a.toUpperCase));
So where did you go wrong and what is this new voodoo?
Problem 1
As stated before, you were attempting to pass the results of a called method as the second parameter of String.prototype.replace(), when instead you ought to be passing a reference to a function
Solution 1
That's easy enough to solve. Simply removing the parameters and parentheses will give us a reference rather than executing the function.
a.replace('f', String.prototype.toUpperCase.apply)
Problem 2
If you attempt to run the code now you will get an error stating that undefined is not a function and therefore cannot be called. This is because String.prototype.toUpperCase.apply is actually a reference to Function.prototype.apply() via JavaScript's prototypical inheritance. So what we are actually doing looks more like this
a.replace('f', Function.prototype.apply)
Which is obviously not what we have intended. How does it know to run Function.prototype.apply() on String.prototype.toUpperCase()?
Solution 2
Using Function.prototype.bind() we can create a copy of Function.prototype.call with its context specifically set to String.prototype.toUpperCase. We now have the following
a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))
Problem 3
The last issue is that String.prototype.replace() will pass several arguments to its replacement function. However, Function.prototype.apply() expects the second parameter to be an array but instead gets either a string or number (depending on if you use capture groups or not). This would cause an invalid argument list error.
Solution 3
Luckily, we can simply substitute in Function.prototype.call() (which accepts any number of arguments, none of which have type restrictions) for Function.prototype.apply(). We have now arrived at working code!
a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))
Shedding bytes!
Nobody wants to type prototype a bunch of times. Instead we'll leverage the fact that we have objects that reference the same methods via inheritance. The String constructor, being a function, inherits from Function's prototype. This means that we can substitute in String.call for Function.prototype.call (actually we can use Date.call to save even more bytes but that's less semantic).
We can also leverage our variable 'a' since it's prototype includes a reference to String.prototype.toUpperCase we can swap that out with a.toUpperCase. It is the combination of the 3 solutions above and these byte saving measures that is how we get the code at the top of this post.
Why don't we just look up the definition?
If we write:
a.replace(/(f)/, x => x.toUpperCase())
we might as well just say:
a.replace('f','F')
Worse, I suspect nobody realises that their examples have been working only because they were capturing the whole regex with parentheses. If you look at the definition, the first parameter passed to the replacer function is actually the whole matched pattern and not the pattern you captured with parentheses:
function replacer(match, p1, p2, p3, offset, string)
If you want to use the arrow function notation:
a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
Old post but it worth to extend #ChaosPandion answer for other use cases with more restricted RegEx. E.g. ensure the (f) or capturing group surround with a specific format /z(f)oo/:
> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.
I just want to highlight the two parameters of function makes finding a specific format and replacing a capturing group within the format possible.
SOLUTION
a.replace(/(f)/,(m,g)=>g.toUpperCase())
for replace all grup occurrences use /(f)/g regexp. The problem in your code: String.prototype.toUpperCase.apply("$1") and "$1".toUpperCase() gives "$1" (try in console by yourself) - so it not change anything and in fact you call twice a.replace( /(f)/, "$1") (which also change nothing).
let a= "foobar";
let b= a.replace(/(f)/,(m,g)=>g.toUpperCase());
let c= a.replace(/(o)/g,(m,g)=>g.toUpperCase());
console.log("/(f)/ ", b);
console.log("/(o)/g", c);
Given a dictionary (object, in this case, a Map) of property, values, and using .bind() as described at answers
const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]);
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));
console.log(str);
Using a JavaScript plain object and with a function defined to get return matched property value of the object, or original string if no match is found
const regex = /([A-z0-9]+)/;
const dictionary = {
"hello": 123,
[Symbol("dictionary")](prop) {
return this[prop] || prop
}
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));
console.log(str);
In the case of string conversion from CamelCase to bash_case (ie: for filenames), use a callback with ternary operator.
The captured group selected with a regexp () in the first (left) replace arg is sent to the second (right) arg that is a callback function.
x and y give the captured string (don't know why 2 times!) and index (the third one) gives the index of the beginning of the captured group in the reference string.
Therefor a ternary operator can be used not to place _ at first occurence.
let str = 'MyStringName';
str = str.replace(/([^a-z0-9])/g, (x,y,index) => {
return index != 0 ? '_' + x.toLowerCase() : x.toLowerCase();
});
console.log(str);

How to set a localStorage item as function?

I would like to set a localStorage item as a function, but when I do it gets saved as a string.
localStorage.getData = function (key="def") {
let data = null;
if (this[key] && !_this.isDataAvailableInList(this[key])) {
data = this[key].data;
}
return data;
};
The above function get stored as string and not function.
Note: Kindly do not provide alternatives or other suggestions, I am well aware of alternatives and am curious to know how this works. I have not provided the entire context so lets just assume I am in need of setting a localStorage item as a function
Unfortunately local storage is only about strings. Even arrays or other data are always converted to strings.
For most dead data without cycles you can use of course JSON.stringify and JSON.parse, but in other cases or for code or other Javascript entities unsupported by JSON (like Infinity or undefined) you need to serialize and deserialize yourself.
Javascript provides eval, so you can store the function f converting it first to a string with f+"" and get back the function with eval(s).
This won't work with closures however and moreover you cannot even know if a function is indeed a closure or not in Javascript (so you cannot even raise an error if the impossible operation of serializing a closure is attempted).
Convert the function into a string via .toString(). When you read it, convert it back to a function via new Function(myString).
Example:
No pamameters:
(new Function('var a = 0; return a + 1;'))() === 1
With parameters:
(new Function(['param0'],'var a = param0; return a + 1;'))(100) === 101
Note: The scope of the original function will be lost.

JavaScript: alert object name as a string

I'm trying to alert any JavaScript object as a string, in a function. This means if the parameter given to the function is window.document, the actual object, it should alert "window.document" (without quotes) as a literal string.
The following calls...
example(window);
example(window.document);
example(document.getElementById('something'));
...calling this function...
function example(o) {/* A little help here please? */}
...should output the following strings...
window
window.document
document.getElementById('something')
I've attempted to do this with combinations of toString() and eval() among some more miscellaneous shots in the dark without success.
No need insane backwards compatibility, newer ECMAScript / JavaScript features/functions are fine. Feel free to inquire for clarifications though the goal should be pretty straight forward.
This is not possible to do in a self contained script.
If using a preprocessor would be an option, then you could write one which converts example(whatever) into example('whatever'). Other than that I'm afraid you're out of luck.
The first problem is that objects don't have names.
The second problem is that from your examples, you're not really wanting to print the (nonexistent) name of an object, you want to print the expression that evaluated into a reference to an object. That's what you're trying to do in this example:
example(document.getElementById('something'));
For that to print document.getElementById('something'), JavaScript would have had to keep the actual text of that expression somewhere that it would make available to you. But it doesn't do that. It merely evaluates the parsed and compiled expression without reference to the original text of the expression.
If you were willing to quote the argument to example(), then of course it would be trivial:
example( "document.getElementById('something')" );
Obviously in this case you could either print the string directly, or eval() it to get the result of the expression.
OTOH, if you want to try a real hack, here's a trick you could use in some very limited circumstances:
function example( value ) {
var code = arguments.callee.caller.toString();
var match = code.match( /example\s*\(\s*(.*)\s*\)/ );
console.log( match && match[1] );
}
function test() {
var a = (1);
example( document.getElementById('body') );
var b = (2);
}
test();
This will print what you wanted:
document.getElementById('body')
(The assignments to a and b in the test() function are just there to verify that the regular expression isn't picking up too much code.)
But this will fail if there's more than one call to example() in the calling function, or if that call is split across more than one line. Also, arguments.callee.caller has been deprecated for some time but is still supported by most browsers as long as you're not in strict mode. I suppose this hack could be useful for some kind of debugging purposes though.
Don't know why you need this, but you can try walking the object tree recursively and compare its nodes with your argument:
function objectName(x) {
function search(x, context, path) {
if(x === context)
return path;
if(typeof context != "object" || seen.indexOf(context) >= 0)
return;
seen.push(context);
for(var p in context) {
var q = search(x, context[p], path + "." + p);
if(q)
return q;
}
}
var seen = [];
return search(x, window, "window");
}
Example:
console.log(objectName(document.body))
prints for me
window.document.activeElement

Categories