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
Related
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.
This question already has answers here:
What does the construct x = x || y mean?
(12 answers)
Closed 6 years ago.
In JavaScript I recently realized you could use the OR || logical operator for assignment, and I want to know if it's considered bad practice.
In particular I have some functions that have optional array input, if the input is null or undefined I should just set it to an empty array [], if it has content it should take the content.
I found that using the assignment using the OR operator handles that perfectly in a single line, it's clean. However, it feels like the kind of thing that might be considered bad practice, or may have some horrible pitfalls I'm not considering.
Another approach is a simple if check, which is fairly safe in general.
I want to know if using the || approach seen below has any pitfalls I'm not considering, although it works in this scenario I would appreciate knowing if it works well to keep using this in the future, or to stop using it altogether.
https://jsbin.com/nozuxiwawa/1/edit?js,console
var myArray = ['Some', 'Strings', 'Whatever'];
// Just assign using OR
var pathOne = function(maybeAnArray) {
var array = maybeAnArray || [];
console.log(array);
}
// Assign using IF
var pathTwo = function(maybeAnArray) {
var array = [];
// Covers null and undefined
if (maybeAnArray != null) {
array = maybeAnArray;
}
console.log(array);
}
console.log('Path one:');
pathOne(myArray); // ['Some', 'Strings', 'Whatever']
pathOne(null); // []
console.log('\nPath two:');
pathTwo(myArray); // ['Some', 'Strings', 'Whatever']
pathTwo(null); // []
IMHO the use of the OR || for the purposes of assignment is perfectly valid and is good practice. We certainly use it in our projects and I've seen it used in lots of 3rd party projects that we use.
The thing you need to be aware of is how certain JavaScript objects can be coerced to be other values. So for example, if you're ORing values such as "", false or 0 then they are treated as false... this means that when you have the following:
function f(o) {
var x = o || -1;
return x;
}
Calling:
f(0)
...will return -1... but calling
f(1)
Will return 1 ... even though in both cases you passed a number - because 0 is treated as false -1 is assigned to x.
...that said, as long as you're aware of how the OR operator will treat the operands that you use with it - then it is good JavaScript practice to use it.
i prefer the first option, it's clear for my eyes, but when i need to share my code with others will think about to use second, will be more clear for any.
Now i'm using sonar, and prefer the second option too, will more easy to comprend for machine in inegration works.
Last idea is to use
if(maybeAnArray !== void(0))
Two reasons:
use cast and type conditionals
void(0) will works same for all browsers
Expect it helps yopu
When given the option, I prefer concise code (which must still be readable).
I would say || is common enough that it is considered good practice. Once one has seen it a few times it reads just fine.
In my opinion there are few reasons why you should rather use the second option:
First of all it's much more readable - new developers that are still learning can have problems with understanding notation like var myArray = someArrayArg || [];
If you are using some kind of code checkers like JSLint, they will return warnings and/or errors like Expected a conditional expression and instead saw an assignment. for the statement with var myArray = someArrayArg || [];
We already have something like var myArray = someArrayArg ? someArrayArg : []; that works pretty well
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.
I'm trying to find out which would be the most optimal way of intersection a set of texts and find the common words in them. Given this scenario:
var t1 = 'My name is Mary-Ann, and I come from Kansas!';
var t2 = 'John, meet Mary, she comes from far away';
var t3 = 'Hi Mary-Ann, come here, nice to meet you!';
intersection result should be:
var result =["Mary"];
It should be able to ignore punctuation marks like .,!?-
Would a solution with regular expressions be optimal?
Here's a tested solution :
function intersect() {
var set = {};
[].forEach.call(arguments, function(a,i){
var tokens = a.match(/\w+/g);
if (!i) {
tokens.forEach(function(t){ set[t]=1 });
} else {
for (var k in set){
if (tokens.indexOf(k)<0) delete set[k];
}
}
});
return Object.keys(set);
}
This function is variadic, you can call it with any number of texts :
console.log(intersect(t1, t2, t3)) // -> ["Mary"]
console.log(intersect(t1, t2)) // -> ["Mary", "from"]
console.log(intersect()) // -> []
If you need to support non English languages, then this regex won't be enough because of the poor support of Unicode in JavaScript regexes. Either you use a regex library or you define your regex by explicitly excluding characters as in a.match(/[^\s\-.,!?]+/g); (this will probably be enough for you) .
Detailed explanation :
The idea is to fill a set with the tokens of the first text and then remove from the set the tokens missing in the other texts.
The set is a JavaScript object used as a map. Some purists would have used Object.create(null) to avoid a prototype, I like the simplicity of {}.
As I want my function to be variadic, I use arguments instead of defining the passed texts as explicit arguments.
arguments isn't a real array, so to iterate over it you need either a for loop or a trick like [].forEach.call. It works because arguments is "array-like".
To tokenize, I simply use match to match words, nothing special here (see note above regarding better support of other languages, though)
I use !i to check if it's the first text. In that case, I simply copy the tokens as properties in the set. A value must be used, I use 1. In the future, ES6 sets will make the intent more obvious here.
For the following texts, I iterate over the elements of the sets (the keys) and I remove the ones which are not in the array of tokens (tokens.indexOf(k)<0)
Finally, I return the elements of the sets because we want an array. The simplest solution is to use Object.keys.
Once, I saw an example like this:
var a, x, y;
var r = 10;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
And it looks very convenience, because that way I don't have to type all the Math.s.
But when I take a look at the MDN, it says:
Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.
So is it okay to use with()? In HTML5?
The MDN you linked says Using with is not recommended...
with is an excellent way of making spaghetti code for lunch.
You might like it, but the guy that will need to debug it will curse you.
javascript has some very weird operators, like the comma operator(,).
Can you understand what the following code does?
var a = "a";
var b = "b";
a = [b][b = a,0];
Well it swaps a and b... You don't understand , so as the guy that will need maintain your with code. Don't use hacks, hacks are cool in charades games, not in real code.
When is the comma operator useful?
The comma swap Fiddle
It is okay to use any feature of JavaScript, so long as you understand it.
For example, using with you can access existing properties of an object, but you cannot create new ones.
Observe:
var obj = {a:1,b:2};
with(obj) {
a = 3;
c = 5;
}
// obj is now {a:3,b:2}, and there is a global variable c with the value 5
It can be useful for shortening code, such as:
with(elem.parentNode.children[elem.parentNode.children.length-3].lastChild.style) {
backgroundColor = "red";
color = "white";
fontWeight = "bold";
}
Because the properties of the style object already exist.
I hope this explanation is clear enough.
In his excellent book "Javascript: The Good Parts", Douglas Crockford lists the "with Statement" in Appendix B: The Bad Parts.
He says "Unfortunately its results can sometimes be unpredictable, so it should be avoided".
He goes on to give an example, where an assignment inside the with will operate on different variables depending on whether the object is defined or not.
See With statement considered harmful (but less detailed than the explanation in the book).