There is a function in JavaScript which calculates the sum of some digits, however I don't understand what this part ([].$) means:
const sum = d => d != [].$ ? `${d = [...`${d}`].join` + `} = ${eval(d)}` : ``
[].$
[].$ - what is it?
[].$ - what is it?
It's an empty array literal ([]) followed by a property accessor expression (.$) looking up the property called $.
Since arrays don't normally have a property with that name, presumably it's been set there (or potentially set there, given the check) by some previous code. If no code sets it, then it's a short way to write undefined (since [].$ is undefined when $ isn't a property of arrays).
For this particular code, it doesn't have any significant meaning. It's there just to represent the value undefined with the least amount of characters. It could equally be []._ or just undefined.
For how it's interpreted. #T.J.Crowder answer summarised it pretty well
Related
During my coding I made a mistake by calling a function like this
someFunction( 'abc' [someValue] )
I had forgotten the colon inside the function call.
After I found the error I played around.
An assignment like this does not throw an error as well.
let a = 'abc'[someValue];
I would expect a syntax error here. Is there an explanation for this?
A string in Javascript can behave as an object and, as such has properties such as .length and methods such as .slice. So, for any property access on an object in Javascript, one can use either the dot notation as in:
str.length
or the [] syntax as in:
str["length"]
or using a variable:
let len = "length";
str[len]
So, what you have with:
'abc' [someValue]
Is just that syntax. A string followed by a property access. That is legal Javascript. It attempts to get the property from that object with the name of whatever string is in the someValue variable.
Here's are a couple working examples:
// simple property access
let prop = "length";
console.log("abc"[prop]);
// method call
console.log("one fine day"["slice"](4, 8));
One would not generally code this way with a string, but it's perfectly legal as it's just part of how one can access properties on an object in Javascript.
Because that's not a syntax error. The engine thought you were trying to get a letter from that string, so if someValue was a number it will work perfectly fine
let a = "abc"[0]
console.log(a, "abc"[1]) //a, b
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);
Regarding
p = 0;
(p+1)++;
> ReferenceError: Invalid left-hand side expression in postfix operation
and
p = 0;
++(p+4);
> ReferenceError: Invalid left-hand side expression in prefix operation
I just got a bit of a surprise, as I expected postfix/prefix operators to be ok with working on the resolution of the expression (brackets have the highest operator precedence).
Could someone give me with a line or three to explain what is happening here?
Thanks
EDIT: Thanks for the quick responses, first answer marked as the answer. I feel I should also point people to the indepth answer from #thefourtheye below
++ increments the value of a variable, so it is larger than before. Eg:
var x = 3;
x++;
alert(x); // will show 4
For there to be any point for this, the expression to the left of ++ must be accessible and mutable, otherwise the increment would be possible. Eg:
3++
doesn't make any sense, as 3 is a constant and can't be incremented. We don't want this to be possible:
3++;
alert(3); // outputs 4???
This is why your expression doesn't work. Ie:
var p = 2;
(p + 1)++;
has the same problem as above. (p + 1) will evaluate to 3, and ++ can't change the value of the constant 3.
You are trying to increment (), the increment/decrement operator can be apply on variable, try the sample code
(p++) + 1
OR
(++p) + 1
Remember that when you write p++, that actually gets translated to p = p + 1. The operators ++ and -- are convenience notation for incrementing/decrementing a variable for future use. But how is (p+1)++ or ++(p+4) supposed to be translated? Those sort of imply that 1 or 4 are being incremented/decremented for future use, which doesn't make sense.
When you have an expression like this
(expr)++;
These are the operations JavaScript will do internally
Resolve the actual object referenced by expr.
This step is important, because you can even do something like this
var a = {b: 1};
++a.b;
a.b++;
console.log(a.b);
# 3
Now, JavaScript has to resolve the actual object to be incremented. In this case, it will be b in a.
Get the value at the reference and convert that value to a Number.
This step is also very important, because you may even have values like this
var a = {b: '1'};
console.log(++a.b);
# 2
JavaScript will try its best to get a number value, instead of failing immediately.
Increment the number.
Store the new vale in expr. This is step where your expression is failing.
In your case expr is p + 1, when it is resolved the value would be just a numeral, whose value can never be changed. (You can never change the value of 1 to something else). So, after the incrementing part, when the new value has to be stored back, JavaScript doesn't find a valid reference to store it. That is why it throws this error.
ReferenceError: Invalid left-hand side expression in postfix operation
This error message is actually thrown from internal PutValue method. The very first step goes like this
If Type(V) is not Reference, throw a ReferenceError exception.
Reference: ECMA Script 5.1 Standard Specification for Prefix Increment Operator
I want to get a string represention of any object or value in JavaScript. I did a couple of experiments.
> var a = document.createTextNode('foo'); a
"foo"
> var a = document.createTextNode('foo'); a.toString()
"[object Text]"
> var a = 1; a.toString()
"1"
> (1).toString()
"1"
> 1.toString()
SyntaxError: Unexpected token ILLEGAL
I have the following questions:
Why does 1.toString() fail?
Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Is there any other alternative to the function str I have written in the previous point?
1). Why does 1.toString() fail?
The JavaScript parser only uses a 1 character lookahead and can't determine if that's 1.0 or 1.toString(). You can use 1..toString() to get around that.
2). Will the following function return me a string representation of every possible JavaScript object, value or literal? Function: function str(a) {return a.toString()}
Any literal will be converted to a temporary object in order to have its toString() called. If the object has its own toString() defined, it will be called. Otherwise, it will use Object.prototype.toString() (having gone up the prototype chain) for almost all cases (the other case is an object with a null prototype).
3). Is there any other alternative to the function str I have written in the previous point?
Yes. You can invoke the toString() implicitly by concatenating an empty string, e.g. 1 + "". You can also use the String constructor, e.g. String(value) (thanks T.J. Crowder). The advantages of these other ones is no exception will be thrown if you attempt to call toString() on null or undefined.
However, these tricks will convert null and undefined to their string equivalents (almost never what you want). One dirty trick is to put the value in a literal array, e.g. [value] and then call toString() on it. This will actually invoke join(","), but seeing as it only has one member, the comma will never become part of the string.
The real power of doing this is that null and undefined will just become an empty string. If that's OK for your program, then it can be useful. Keep in mind to comment this solution as it's not immediately obvious what this code is doing. Alternatively, check value == null which will detect null and undefined and handle it appropriately.
However, if you're wanting a string in order to classify a value, you can get the type's [[Class]] like so...
var getInternalClass = function(value) {
return Object.prototype.toString.call(value).slice(8, -1);
};
This will invoke the Object's toString() and set the ThisBinding to the value provided as the argument. This is the only way to expose an object's internal [[Class]]. The advantage of this (over typeof, for example) is that primitives and objects will always return the same value (with the primitives being converted to temporary objects, boxed by the call() context in non-strict mode).
for 1.toString(), you need to do:
1 .toString() //add space before dot (.) to avoid taking it as decimal
shortest way (alternative to function str ) to convert to string is:
var str = str + '';
Your str(a) function is correct but it will call the default implementation of toString() inherited from Object. So yes, your function will give you string representation of every JS object but not in way you want it. You need to override it.
var o = new Object();
o.toString(); // returns [object Object]
See here for reference and overriding: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
function doIt()
{
var person={firstname:"John", lastname:"Smith", age:"25"};
var x;
var txt="";
for (x in person)
{
txt=txt+person[x] +"<br>";
}
document.getElementById("showtext").innerHTML=txt;
}
My question is: Why when I replace
txt=txt+person[x]+"<br>";
with:
txt=txt+person.x+"<br>";
the value of person.x is returned as undefined?
In the first iteration of the loop, x should be 'firstname'. So person.x should be equal to person.firstname, and thus return the value John. I would love to understand why it returns 'undefined' instead.
In the first case you're using 'bracket notation', where the value of the variable x is used to determine the property name.
In the second case you're using 'dot notation', where the property looked for is literally called x.
The Answer is:
Since x ist not the property name and the object doesnt have a property with the name/key x.
person.x
is undefined.
This would be equivalent to
person["x"] (the subtle difference lies in the double quotes)
what is also undefined.
For it to work with the dot-notation, you would have to write :
eval("person." + x); // but this is evil
// Tested on win7 with chrome 45+
so expression eval("person." + x) would expand in the first run to eval("person.firstname" ) which returns "John"
...
What I don't recommend, because eval can introduce may security issues.
Update 1
Disclaimer:
With this answer i only answered the initial question, and tried to explain the problem. With "// but this is evil " i am suggesting not to use this approche.
When you write person[x], it means "look up the value of x, and then find that element in person". When you write person.x it means "look up the value of x inside of person".
person doesn't have an x element, so you're getting undefined. You really do just want person[x].
x will be a string. eg "person" so you have to use [] brackets
You can't use dot notation with a variable key. It will look up the property "x" which is undefined. person[x] is the right way.
Javascript will allow you to access an object's property using a variable if you use the square brackets syntax. Thus, person[x] will do what you are trying to do as long as x contains a string representing the property name. The syntax construction person.x is equivalent to person["x"].