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.
Related
How can I create a new function from a string when the string contains if condition?
The string will comes from outside but in the dummy example below everything is hardcoded to be easier.
let f1: number = 1;
let f2: number = 0;
let condition: string = 'if(this.f1===1){this.f2 = 1}';
let result = this.createFn(condition);
createFn(param: string) {
return new Function('return ' + param)();
// or return new Function(param)();
}
Of course this is not working and I'm searching a way to do it.
I don't want to use eval().
Passing a string to the constructor of Function is almost the same as using eval().
To safely execute arbitrary code in JavaScript you need to have a JavaScript interpreter written in JavaScript which will then execute the received string in a sandbox environment. A quick google search yields this package: https://github.com/NeilFraser/JS-Interpreter.
Others who faced this issue have decided to implement themselves a domain-specific subset of a programming language to then allow the strings to execute that language's code. e.g. SAP's SAPUI5/OpenUI5 solution for the Expression Binding they have.
Given a JavaScript expression as a string, how can I extract the names of variables used inside the expression, and going beyond that, extract the names of any properties accessed on these variables?
These expressions are written by another author using their own personalized names like cabbage and elephant.weight.
The expression could be simple, like: firstName, name.charAt(0), c.radius * Math.PI
But could also be something complex like:
(function(){
var fruit = apple || banana.length;
if (fruit > citrus.lime) {
fruitSalad += "," + citrus.lemon;
// ...
return lemon.split(/[0-9]+/);
}
return fruitBowl.width * fruitBowl['amount'] * Math.SQRT2;
})();
Running the above expressions will throw reference errors unless variables like apple are defined. If I have a way of parsing the expression and extracting the undefined variable names, I can take note of them, and run the expression at a later time without any issues, with values for each variable.
Additionally, if I can take note of which variables have properties and the names of those properties, then I can initialize these also.
fruit = apple = banana = fruitSalad = "";
citrus = { lemon: "0", lime: "1" };
fruitBowl = { width: "10", amount: "30" };
// run the expression
Stuff I've Tried
Simply running the expression and using try/catch to catch each reference error, parsing the exception text for the variable name. This is perilous as exception text is different in each browser. This also doesn't account for parts of the expression which aren't immediately evaluated such as in isTrue? yesText : noText. As accessing undefined properties of objects does not yield an error, these can't be noted.
Parsing the expression into an AST using a library such as Esprima. This is my first time using AST (Abstract Syntax Tree). I think I can access the identifiers used in the code but there doesn't seem to be any clear distinction between variable names and property names. As the output is quite verbose, it would be easy to let something slip. I have looked into AST walkers, but nothing stands out as being a solution.
Note: Assume that eval will not be used. Variables will always be strings or objects whose properties are strings, so knowing the type of variable is not an issue, only the names used. Names of functions used in the expressions can be ignored.
Preface
The answer to this question may very well be "because the creators of JavaScript decided so." Mostly I'm just curious if there's a specific reasoning behind this decision, aside from mere consistency with other identifiers/variable names and other languages.
Question
This is legal:
var foo = { "1abc": "bar", 1: "lol" };
document.write(foo["1abc"]);
document.write(foo[1]);
Lines 2-3 of this are not legal:
var foo = { "1abc": "bar", 1: "lol" };
document.write(foo.1abc);
document.write(foo.1);
I have not used a language where identifiers can start with a number, but until JavaScript, I also have not used a language where dictionary values could be referenced using both dict[index] and dict.index syntax.
I'm not sure if dictionary keys are considered identifiers or not. If they are, then this question is a no-brainer.
Then again, I'm not really clear why JavaScript identifiers can't start with a number, given that numbers are a Number type. In other languages (e.g. C#) numbers have types like int, long, double, etc causing 5L or 5D to specify "5 as long" and "5 as double" respectively. So, in theory, JavaScript identifiers could start with a number, and be fine (I think).
C#:
var x = 5L; // x is long type
var y = 5D; // x is double type
JavaScript:
var x = 5L; // syntax error
var y = 5D; // syntax error
So, my guess is that JavaScript identifiers can't start with a number for consistency with other languages, and JavaScript dictionary keys can't be referenced with dict.123 syntax for consistency with other JavaScript identifiers. Is this the only reason?
I think you answered your own question. "because the creators of JavaScript decided so."
From msdn: https://msdn.microsoft.com/en-us/library/ie/67defydd%28v=vs.94%29.aspx
Lastly, what you are dealing with are object literals, not dictionaries. Javascript has two ways of retrieving member properties for different occasions. "." syntax is for static access, "[]" is for dynamic access. Consider the following:
var myObj = {
x:"foo",
getX: function() {return this.x;}
};
var get = "get";
var X = "X";
alert(myObj[get+X]()); //Alerts "foo";
Javascript lets you do some pretty dynamic things, that usually result in horribly unmaintainable code, but its still pretty cool.
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.
a="12345"
a[2]=3
a[2]='9'
console.log(a) //=> "12345"
What is going on?? This quirk caused me 1 hour painful debugging. How to avoid this in a sensible way?
You cannot use brackets to rewrite individual characters of the string; only 'getter' (i.e. read) access is available. Quoting the doc (MDN):
For character access using bracket notation, attempting to delete or
assign a value to these properties will not succeed. The properties
involved are neither writable nor configurable.
That's for "what's going on" part of the question. And for "how to replace" part there's a useful snippet (taken from an answer written long, long ago):
String.prototype.replaceAt = function(index, char) {
return this.slice(0, index) + char + this.slice(index+char.length);
}
You may use as it is (biting the bullet of extending the JS native object) - or inject this code as a method in some utility object (obviously it should be rewritten a bit, taking the source string as its first param and working with it instead of this).
According to this question, this is not supported among all browsers.
If your strings aren't too long, you can do this relatively easy like that:
var a="12345";
a = a.split("");
a[2]='9';
a = a.join("");
console.log(a);
var letters = a.split('');
letters[2] = 3;
letters[2] = 9;
console.log(letters.join(''));
http://jsfiddle.net/XWwKz/
Cheers