📜 ⬆️ ⬇️

Seven amazing "features" Javascript

Over the past few months, I have made a few JSHint for JSHint , mainly with the goal of exploring ES6 (I'm particularly proud of how the scope scans for variables are redone). During this process, I came across a few things that surprised me - mostly in ES6, but there is also something about ES3 that I have never used before.

Break from any block


Surely you know that in any cycle you can use the keywords break and continue - this is a standard feature in modern programming languages. However, not everyone knows that cycles can be given labels and with their help interrupt any particular cycle:

 outer: for(var i = 0; i < 4; i++) { while(true) { continue outer; } } 

The same applies to break . You have probably seen how it is used in a switch :
')
 switch(i) { case 1: break; } 

Generally speaking, this is why Crockford does not advise adding indents before case - the expression break throws out of the switch block, not case , but for me the option with indents seems more readable. Switch switch can also be tagged with:

 myswitch: switch(i) { case 1: break myswitch; } 

You can also declare blocks just like that. I know that this is also available in C #, and probably in other languages ​​too:

 { { console.log("   "); } } 

If you put it all together, you can exit any block using a label:

 outer: { inner: { if (true) { break outer; } } console.log("    "); } 

Of course, this only applies to break - the continue statement is valid only inside the loop. I have never seen labels in Javascript code - most likely, because if you suddenly need to urgently exit more than one block, this is a reason to rewrite the code to a function with return .

However, if I suddenly wanted to write a function with a single exit point (which, generally speaking, is not to my taste), one could use this approach. Here, for example, is a function with multiple exit points:

 function(a, b, c) { if (a) { if (b) { return true; } doSomething(); if (c) { return c; } } return b; } 

Add tags, and it turns out this:

 function(a, b, c) { var returnValue = b; myBlock: if (a) { if (b) { returnValue = true; break myBlock; } doSomething(); if (c) { returnValue = c; } } return returnValue; } 

Or, one could use more blocks:

 function(a, b, c) { var returnValue = b; if (a) { if (b) { returnValue = true; } else { doSomething(); if (c) { returnValue = c; } } } return returnValue; } 

In general, the option with labels I like the least, but maybe only because I was not used to it?

Destructuring an existing variable


First - a trick that I can not explain. In ES3, apparently, you can add parentheses around the variable when assigning and it will work:

 var a; (a) = 1; assertTrue(a === 1); 

If you know why someone might need to do this, please write in the comments.

Destructuring is the process of getting the value of a variable from an object or array. Most often you can see a similar example:

 function pullOutInParams({a}, [b]) { console.log(a, b); } function pullOutInLet(obj, arr) { let {a} = obj; let [b] = arr; console.log(a, b); } pullOutInParams({a: "Hello" }, ["World"]); pullOutInLet({a: "Hello" }, ["World"]); 

But you can do the same without let , var and const . For an array, just write like this:

 var a; [a] = array; 

But with the object does not work - it must be wrapped in parentheses:

 var a; ({a} = array); 

The reason is that it gives too much room for ambiguous interpretation and errors associated with anonymous blocks of code, because the automatic placement of semicolons turns identifiers into calculated expressions, and they can have side effects:

 var a = { get b() { console.log("!"); } }; with(a) { { b } } 

Returning to the original example, where we have concluded the assignment in parentheses - contrary to assumptions, this has nothing to do with restructuring:

 var a, b, c; (a) = 1; [b] = [2]; ({c} = { c : 3 }); 

Destructuring with numbers


Another aspect of destructuring that not everyone can suspect is that property names do not have to be unquoted lines. These can be numbers:

 var {1 : a} = { 1: true }; 

Or quoted strings:

 var {"1" : a} = { "1": true }; 

And you can also calculate the property name from the expression:

 var myProp = "1"; var {[myProp] : a} = { [myProp]: true }; 

This makes it easy to write very convoluted code:

 var a = "a"; var {[a] : [a]} = { a: [a] }; 

Class declarations are block-bound


Function declarations rise to the very top of the block, which allows them to be used before the declaration:

 func(); function func() { console.log("  "); } 

But if a function is declared during the assignment of a variable, then only the declaration of the variable is raised, but not the value assigned to it:

 func(); // func ,    ,   "func   " var func = function func() { console.log("  "); }; 

Classes are one of the most popular parts of the ES6 specification, and have always been considered a kind of syntactic sugar for functions. However, if you think that this code will work, then you are mistaken:

 new func(); class func { constructor() { console.log("  "); } } 

Despite the similarities with the first example, it does not work. This is actually the equivalent of the following code:

 new func(); let func = function func() { console.log("Fine"); } 

Here we are trying to access func inside the temporary dead zone , which is a syntax error.

Parameters-namesake


I assumed that a function cannot have two parameters with the same name - but in fact it can!

 function func(a, a) { console.log(a); } func("", ""); //  "" 

However, in strict mode, everything is not so:

 function func(a, a) { "use strict"; console.log(a); } func("", ""); //  Chrome   - SyntaxError: Strict mode function may not have duplicate parameter names 

typeof operator unsafe


Okay, okay, I stole this observation , but it will not be superfluous to repeat it anyway.

Before ES6, it was widely known that using the typeof operator, you can safely find out if an identifier is declared, even if it is not assigned a value:

 if (typeof Symbol !== "undefined") { // Symbol  } //    ,  Symbol   if (Symbol !== "undefined") { } 

But now it only works if you have not declared a variable using let or const . It’s all the fault of VSW , due to which accessing a variable before assigning it is a syntax error, even though “under the hood” the declaration of the variable still rises to the very top of the block.

 if (typeof Symbol !== "undefined") { // Symbol  } let Symbol = true; //      ! 

Array creation


I have always avoided creating an array using the new keyword. Basically, because arguments can be either the length of an array, or its elements:

 new Array(1); // [undefined] new Array(1, 2); // [1, 2] 

However, a colleague recently stumbled upon something that I had never met before:

 var arr = new Array(10); for(var i = 0; i < arr.length; i++) { arr[i] = i; } console.dir(arr); 

This code produces an array with numbers from 0 to 9. And what will happen if you refactor it using map ?

 var arr = new Array(10); arr = arr.map(function(item, index) { return index; }); console.dir(arr); 

The array remained unchanged. Apparently, a constructor that takes a length creates an array and sets the length property, but does not create any elements. Therefore, you can access the property, but you cannot list the elements. And if you set the value of any element?

 var arr = new Array(10); arr[8] = undefined; arr = arr.map(function(item, index) { return index; }); console.dir(arr); 

We get an array, where the eighth element is assigned the number 8, but all other values ​​are not specified. If you look at the polyfill code for the map function, it checks the property of the property using the in operator. The same behavior can be achieved using array literals:

 var arr = []; arr[9] = undefined; //   var arr = []; arr.length = 10; 

Other pearls


There is a great article on the Mozilla developers blog about functions with arrows , which state that comments can be marked with the symbol <-- . Not bad and read the rest of the blog posts.

Source: https://habr.com/ru/post/261785/


All Articles