What do braces do in JavaScript? - javascript

I read this line in Eloquent JavaScript and would love some clarity.
...braces have two meanings in JavaScript. At the start of a statement, they start a block fo statements. In any other position, they describe an object. Fortunately, it is almost never useful to start a statement with a brace object, and...
So, braces in let's say a..... an 'if statement' create a block of statements to execute, but braces that appear in let's say a function call (as a parameter) or in a variable assignment becomes an object literal.
Is that right? What are all the other cases? I'm not sure I understand the rule for when braces bundle up statements and for when they describe an object.

as object literals
var a = {field1:value1, field2:value2}
as function bodies
function func() {
// do something
}
var f = function() { /* do something */ };
var f = ()=>{}; // ditto
where the first item in the statement is an object literal
// {}.toString(); // syntax error, first brace of statement = code block
({}).toString(); // correct
as destructured assignment
var obj = {a:1, b:2, c:3};
var {a:x, c:y} = obj; // assign obj.a to x, and obj.c to y
({a:x, c:y} = obj); // ditto
Note - this has a lot of forms so I won't cover them all, full info found here (thanks RobG)
how this is interpreted
You can assume that all your JS code is inside some {} block. So the start of your code is immediately after a { always.
Wherever a value is expected, {} does not mean a function body. At the start of a statement this is ambiguous because you can have anonymous code blocks like so:
var x = 1;
{
var x = x+2;
// x = 3
}
// x = 3 (!)
This is archaic from C-style syntax where this would influence scope, but in testing this in JS it doesn't seem to have that effect, so for all intents it's rather useless syntax except to identify a code block. If you wanted such behavior you'd need to do this:
var x = 1;
(()=>{
var x = x+2;
// x = 3
})()
// x = 1
If we need an object first in some statement, we need to clarify to JS that we want a value. This is why we use ({}) instead of {}, because the former is unambiguously an object literal inside parens.
a simpler explanation
Rather than examine when {} is parsed as a value, let's look at when it isn't. There are two cases in general where we don't treat {} as an object literal: as a function body or as a statement group (my own term).
Consider the general control statements - if, for, while, with etc. These can all* be used in a way that completely avoids {}. In this respect {} should be thought of as statement groups hence the term.
if (x) x++; else x--;
if (x) {x++;} else {x--;}
{if (x) {x++;} else {x--;}}
*note: switch is an exception, switch(1); gives an error SyntaxError: missing { before switch body
Using this rule it then makes sense why we must use () to denote an object literal if it's the start of a statement - we can't start a statement in (), so you can't start a statement group there either, leaving only one option (object literal or related syntax).
This leaves function bodies.
function bodies
First, consider a function declaration statement:
function f () {}
It doesn't need a semicolon (;). This means the entire thing is a single statement. This explains why the following gives a syntax error in the first form but not the second:
function(){return 1;}(); // error: function statement requires name
var x = function(){return 1;}(); // fine
This is because the first is parsed as a statement, and a function declaration statement cannot be anonymous. However the second is in a value context and is treated as such. The situation is identical as with object literals, if it could be a statement it cannot be a value, but if we're already knee deep in value land, it has to be a value.
The => notation is, with one exception, parsed identically to function. The ()=>{} form is identical but in practice differs because this type of function cannot have a this object - it cannot be an object method (doesn't make much sense to) and it cannot construct new objects (it has no prototype), and other quirks as a result. otherwise it's straightforward to see how it's the same as function(){}.
()=>... however is a little different. It's treated as ()=>{return ...}. But, without the explicit } to finish the return statement, the syntax greedily captures the largest expression that would parse as such (not necessarily work). Case in point:
()=>1; // statement; = function that returns "1"
()=>1(); // statement; = function that returns "1()"
(()=>1()); // TypeError: 1 is not a function
(()=>1)(); // what was intended in above (you'd hope)

Related

Why does this expression result in `ReferenceError`?

Why does incrementing the same thing behave differently when I reference it via a variable?
function f() {
return {};
}
let x = {};
x++; // OK
(f())++ // ReferenceError
The difference is due to that when js is evaluating the expression it starts by checking if the left hand side is convertible to a number and by quirkyness an object is converted to Nan whereas a function is is not and js throws an exception when trying to convert it before it is evaluated.
See also here:
https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-postfix-increment-operator
This whole code doesn't make any sense. You cannot increment a object.

JavaScript Function Parameters vs Object Methods

Can someone explain to me the difference of when to use a function by feeding your variables into the parenthesis, and when to tack the function on after the variable with a period, like using the toString() function?
example code
function addMe(a){
a = a+1;
return a;
}
var num = 1;
addMe(num);
num.toString();
I'm not actually sure if my syntax is correct, but I want to know when to feed a variable as a parameter, like how I feed the variable num, to the addMe function. And when to use the function .toString() by putting a period after the variable and typing out the function.
could I have done something like this- provided I built my function correctly?
var num = 1;
num.addMe();
Thanks for the help!
The first is used for simple 'stand alone' functions, while the latter is used for object methods. E.g a number object by default has a toString() method. Some object methods may also require parameters to be passed between the parentheses.
Variables (a function declaration is just a function stored in a variable) are looked up in the scope chain (going up to the next outer scope until a variable with the name is found):
let a = 1; // outer scope
{ // inner scope
console.log(a); // looked up in "inner scope", than "outer scope"
}
Properties of an object are looked up in the objects prototype chain, so if you do
a.b
then a gets looked up in the scopes as explained above, then b is accessed on the resulting object (everything is an object in JavaScript, except for "nothing" (undefined, null)) by looking up the prototype chain. For a simple object, the chain is quite short:
const a = { b: 1 }; // object -> Object.prototype
Here b will be found in the object itself. However all objects inherit from the Object.prototype object, so if you add a property to that (please don't):
Object.prototype.test = 1;
you can then look it up on every object, as the lookup traverses up the prototype chain, and reaches Object.prototype:
console.log({}.test); // 1
Now for numbers (like in your case), they inherit the Number.prototype so you could do:
Number.prototype.addMe = function() {
console.log(this);
};
// two dots are needed to distinguish it from numbers with a fraction (e.g. 1.2)
1..addMe();
That said, now addMe can be called on every number, everywhere in your code. While that might seems useful, it is actually a pain as you don't know where a certain method was added
1..whereDoIComeFrom()
that makes code unreadable and unstructured. Instead if you need a certain functionality multiple times, abstract it into a function, don't touch the native prototypes.
I assume that addMe is just a simplified example, if it isn't, read on:
If you pass an argument to a function in JavaScript, the value will be copied (it is a bit more complicated with non primitives (everything except numbers, booleans etc.)) into the parameter variable of the function called so here:
function addMe(a){
a = a+1;
console.log(a); // 2
return a;
}
var num = 1;
addMe(num);
console.log(num); // 1 ... ?
you actually got two variables (a and num), changing a does not change num. But as you return a you can do:
num = addMe(num);
which copies the value of num into a, then increases a by one and then copues the value of a back to num.
When you did var num = 1 you created a JavaScript object. It looks just like a number but you can think of everything in JavaScript as an object (simplification) and all these objects have different features. So a number has some features, a string has some other features, etc.
You mentioned one feature: toString. Another feature would be toLowerCase.
toString and toLowerCase are functions that come with JavaScript. These functions are then "put on" all of these objects for us to use.
I can have a string variable like
var text = 'MY TEXT'
var lowercaseText = text.toLowerCase()
console.log(lowercaseText) // my text
This code will work because it was decided that the toLowerCase function should work on strings
I can also have an array (list of items)
const list = ['A', 'B', 'C']
const answer = list.toLowerCase()
console.log(answer)
But this code won't work because toLowerCase doesn't work on arrays. So you get the following error message: list.toLowerCase is not a function.
Basically its saying: I don't know what toLowerCase means when used on this list variable (array).
In JavaScript this is called prototypes. Prototype is a way for JavaScript to get some feature from another. Basically: I have all kinds of functions, what object can use what functions. This is called the prototype chain.
In both cases you are using a function. addMe is a function you created and toString is a function in JavaScript that has been placed on objects through this prototype-chain.
Im not actually sure if my syntax is correct
Yes your syntax is correct. Your addMe function is the standard way to create a function in JavaScript.
But i want to know when to feed a variable as a parameter, like how i
feed the variable num, to the addMe function.
Just like you did, you define a function and parameters like you did.
..and when to use the function .toString() by putting a period after
the variable and typing out the function.
When you want to place your function on a object so that all instances of that object can you that object.
In most cases, espcially when you are starting out. You don't have to worry about these prototypes. The way you did.
function addMe(number) {
return number+1
}
const answer = addMe(1) //2
Is a standard way of defining a function and calling it.

Sanitizing `eval` to prevent it from changing any values

This is front-end only, and not back-end. I also acknowledge that this is a bad idea. At this point I'm just curious.
I have a table of records. I would like the user to be able to enter a JavaScript conditional statement, which is then applied to the table to filter the records.
For example, to filter out records with a name that's less than 6 characters, I might enter:
record.name.length < 6
Without using an external library, the easiest way I've found to do this is with eval. However, in using eval, I of course introduce the risk of the user breaking the code (not a huge concern since this is front-end only, but still a user experience issue).
I would like to sanitize the user input so that it cannot change any values. So far, I believe I only need to do these two things to make eval "safe":
Turn any single equals signs = into double or triple equals signs
Remove or escape parentheses ( )
With these two items taken care of, is there anything else I need to do to prevent the user input from changing values?
One way of doing this which is safer than eval is using the Function constructor. As far as I know, this answer is totally safe, but it's quite possible there's some caveat I don't know or have forgotten, so everyone feel free to reply if I'm wrong.
The Function constructor allows you to construct a function from its string and a list of argument names. For example, the function
function(x, y) {
return x + y;
}
could be written as
new Function('x', 'y', 'return x + y;')
or simply
Function('x', 'y', 'return x + y;')
Note that although the function body has access to variables declared in the function definition, it cannot access variables from the local scope where the Function constructor was called; in this respect it is safer than eval.
The exception is global variables; these are accessible to the function body. Perhaps you want some of them to be accessible; for many of them, you probably don't. However, there is a way round this: declare the names of globals as arguments to the function, then call the function overriding them with fake values. For example, note that this expression returns the global Object:
(function() { return Object; })()
but this one returns 'not Object':
(function(Object) { return Object; })('not Object')
So, to create a function which does not have access to any of the globals, all you have to do is call the Function constructor on the javascript string, with arguments named after all the globals, then call the function with some innocuous value for all the globals.
Of course, there are variables (such as record) which you do want the javascript code to be able to access. The argument-name arguments to Function can be used for this too. I'll assume you have an object called myArguments which contains them, for example:
var myArguments = {
record: record
};
(Incidentally, don't call it arguments because that's a reserved word.) Now we need the list of names of arguments to the function. There are two kinds: arguments from myArguments, and globals we want to overwrite. Conveniently, in client-side javascript, all global variables are properties in a single object, window. I believe it's sufficient to use its own properties, without prototype properties.
var myArgumentNames = Object.keys(myArguments);
var globalNames = Object.keys(window);
var allArgumentNames = myArgumentNames.concat(globalNames);
Next we want the values of the arguments:
var myArgumentValues = myArgumentNames.map(function(key) {
return myArguments[key];
};
We don't need to do the values part for the globals; if we don't they'll just all be set to undefined. (Oh, and don't do Object.keys(myArguments).map(...), because there's a (small) chance that the array will come out in the wrong order, because Object.keys doesn't make any guarantees about the order of its return value. You have to use the same array, myArgumentNames.) Then call the Function constructor. Because of the large number of arguments to Function it's not practical to list them all explicitly, but we can get round this using the apply method on functions:
var myFn = Function.apply(null, allArgumentNames.concat([jsString]))
and now we just call this function with the argument list we've generated, again using the apply method. For this part, bear in mind that the jsString may contain references to this; we want to make sure this doesn't help the user to do something malicious. The value of this inside the script is the first argument to apply. Actually that's not quite true - if jsString doesn't use strict mode, then trying to set this to undefined or null will fail, and this will be the global object. You can get round this by forcing the script into strict mode (using '"use strict";\n' + jsString), or alternatively just set this to an empty object. Like this:
myFn.apply({}, myArgumentValues)
I am sharing my implementation (based on #David's answer).
Some of the keys of the Window object might break the Function.apply. This is why I've filtered the ones that break. Explanations in the code below as a comment.
// Why is windowKeys not inside function scope? No need. It won't
// be changing on each call. Creating array with +270 items for each eval
// might effect performance.
const windowKeys = Object.keys(window).filter((key) => {
// Why is window filtered?
// There are some cases that parameters given here might break the Function.apply.
// Eg. window keys as numbers: '0', (if there is iframe in the page)
// the ones that starts with numbers '0asdf',
// the ones that has dash and special characters etc.
try {
Function.apply(null, [key, "return;"]);
return true;
} catch (e) {
return false;
}
});
/**
* evaluates
* #param {string} code
* #param {object} context
* #returns
*/
const safeEval = (code, context) => {
const keys = Object.keys(context);
const allParams = keys.concat(windowKeys, [`"use strict"; return ${code}`]);
try {
const fn = Function.apply(null, allParams);
const params = keys.map((key) => context[key]);
return fn(...params);
} catch (e) {
console.log(e);
}
};
// simple expression evaluation
const res = safeEval("a + b", { a: 1, b: 2 });
console.log(res);
// try to access window
const res1 = safeEval("{a, b, window, document, this: this}", { a: 1, b: 2 });
console.log(res1);
Idk. if this approach can be exploited, if it does. I think another approach can be running eval on cross-domain iframe and get the result with window messages.

Order of evaluation in if statement in with ES6 destructuring

I recently stumbled upon the new Destructuring Javascript feature that is available with ES6.
Found out a weird situation where i am not really sure of what is going on. Hopefully you guys will help me understand.
If i type this in my console:
var car={}
var {undefinedProp: es6Magic} = car;
I get an undefined. Looks fair to me, since car has no defined property.
But if i use an If statement around it i get a different and unexpected result:
function testPriorities() {
var car = {}
if ({
undefinedProp: es6Magic
} = car) {
console.log('how do i even get here', es6Magic);
}
}
What the hell?
Why is that es6Magic is assigned with an undefined value and it still returns true?
What rules are being applied when running the if statement?
If i type this in my console:
var car={}
var {undefinedProp: es6Magic} = car;
I get an undefined.
But not because es6Magic has an undefined value (it does, I mean it's not the reason). It's because variable declarations have no result value, and your complete snippet does not have a result (unlike expression statements).
But if i use an If statement around it i get a different and unexpected result:
var car = {}
if ({undefinedProp: es6Magic} = car) {
console.log('how do i even get here', es6Magic);
}
I guess that is true because the car exists, but why does it evaluate differently from the console?
Actually you'll still get the undefined result from the last statement, after the console.log output.
And yes, the if condition evaluates to a truthy value because car exists - that's what assignment expressions always do. This doesn't even have anything to do with destructuring, … = car always evaluates to the right hand side car regardless what the left hand side target expression is.
You can also try
> var car = {}, es6Magic;
undefined
> ({undefinedProp: es6Magic} = car); // no `var` - plain assignment!
[object Object]
The console shows undefined because a variable declaration doesn't return anything, it declares a variable.
The second version works because {foo: bar} is interpreted as an object literal, which you are assigning to. That returns the object, which is truthy. I would expect that to throw an error, which it does in the console:
Uncaught SyntaxError: Invalid destructuring assignment target
The literal shouldn't be a valid target, but a transpiler would most likely break that.
The if statement executes because you are basically doing assignment inside if statement which always return the assigned value which in this case is {} which evaluates to true.
var b
if(b={}){console.log("what!!")}
function a(){
var car={}
return {undefinedProp: es6Magic} = car;
}
console.log(a())

JavaScript : Expected and assignment or function call and instead saw an expression

I am using JSHint to ensure my JavaScript is "strict" and I'm getting the following error:
Expected an assignment or function call and instead saw an expression
On the following code:
var str = 'A=B|C=D'
var data = {};
var strArr = str.split( '|' );
for (var i = 0; i < strArr.length; i++) {
var a = strArr[i].split('=');
a[1] && (data[a[0].toLowerCase()] = a[1]); // Warning from JSHint
}
Any ideas why I'm getting such an error or how I can code to remove the error.
Here is a simplified version that gives the same warning:
var a, b;
a && (b = a);
Expected an assignment or function call and instead saw an expression
This means that you have an expression but do not assign the result to any variable. jshint doesn't care about what the actual expression is or that there are side effects. Even though you assign something inside of the expression, you are still ignoring the result of the expression.
There is another error by jslint if you care about it:
Unexpected assignment expression
This warns you that you may want to use == instead of = inside logical expressions. It's a common error, therefore you are discouraged to use assignments in logical expressions (even though it is exactly what you want here).
Basically, jshint/jslint do not like misuse of shortcut evaluation of logical operator as replacement for if statements. It assumes that if the result of an expression is not used, it probably shouldn't be an expression.
http://jshint.com/docs/options/#expr - JSHINT says, Expr warnings are part of relaxing options. So, if you write /* jshint expr: true */, it won't give you the warning. But, you have to know the scope of function too. If you just type this line on top of everything, it will apply this rule globally. So, even if you made a mistake on the other lines, jshint will ignore it. So, make sure you use this wisely. Try to use if for particular function ( i mean inside one function only)

Categories

Resources