What is the use case for var in ES6? - ecmascript-6

If the let keyword introduces a proper implementation of block scope, does var any longer have a use case? I am looking at this from a software design standpoint rather than a syntactical, "well you could" standpoint.

If the let keyword introduces a proper implementation of block scope, does var any longer have a use case?
There could be one use case: let declarations in global scope don't create a property on the global object. Example:
"use strict"; // for chrome
var foo = 42;
let bar = 21;
console.log('window.foo (var)', window.foo); // 42
console.log('window.bar (let)', window.bar); // undefined
From 8.1.1.4 Global Environment Records
The object Environment Record component of a global Environment Record contains the bindings for all built-in globals (clause 18) and all bindings introduced by a FunctionDeclaration, GeneratorDeclaration, or VariableStatement contained in global code. The bindings for all other ECMAScript declarations in global code are contained in the declarative Environment Record component of the global Environment Record.
However, this can also easily be solved by creating an explicit global variable using by assigning to the global object directly:
window.foo = 42;
This would also be the only way to create global classes btw, because the class declaration has the same behavior.
(Note: I'm not advocating the use of global variables)
There are syntax constructs where you can only use var, but that's more a consequence of the how the spec evolved and doesn't really serve any practical purpose. For example:
if (true)
var foo = 42; // valid but kind of useless or bad design
// vs
if (true)
let foo = 42; // invalid
Block scope is not the only useful feature though. The temporal dead zone is another handy feature to find bugs more easily. Compare:
var foo = 42;
function bar() {
console.log(foo); // undefined
var foo = 21;
}
bar();
// vs
var foo = 42; // or `let`, doesn't matter
function bar() {
console.log(foo); // ReferenceError, temporal dead zone
let foo = 21;
}
bar();
You get a reference error when trying to access a let variable that wasn't initialized yet.

let can't be used in global scope yet. var can.
This is what you get from Chrome when you try a global let outside of strict mode:
Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

Practically there may be some use-cases.
1. Declare variable in try-catch like so:
try {
//inits/checks code etc
let id = getId(obj);
var result = getResult(id);
} catch (e) {
handleException(e);
}
//use `result`
With let the result declaration would be before try, - a bit early and out of context.
2. Same for conditional declarations:
if (opts.re) {
var re = new RegExp(opts.re);
var result = match(re);
if (!result) return false;
}
// result is available here
With let this code would be forced to complain "good style", though that might be impractical.
3. Loop block:
for (var x = 0; x < data.width; x++) {
if (data[x] == null) break;
//some drawing/other code
}
//`x` pointing to the end of data
Some may consider that untidy, I myself prefer lets, but if code I already has vars - that's natural to keep using them.

You can use var if you want to deconstruct something into the function scope, for example a conditional:
if (Math.random() > 0.5)
var {a,b} = {a: 1, b: 2}
else
var {a,b} = {a: 10, b: 20}
// Some common logic on a and b
console.log(a, b)
With let you would have to write something like
let result;
if (Math.random() > 0.5)
result = {a: 'foo', b: 'bar'}
else
result = {a: 'baz', b: 'qux'}
// Using const might make more sense here
let {a, b} = result;
// Some common logic on a and b
console.log(a,b)

Don't Throw Out var
Stylistically, var has always, from the earliest days of JS, signaled "variable that belongs to a whole function." var attaches to the nearest enclosing function scope, no matter where it appears. That's true even if var appears inside a block:
function diff(x,y) {
if (x > y) {
var tmp = x; // `tmp` is function-scoped
x = y;
y = tmp;
}
return y - x;
}
Even though var is inside a block, its declaration is function-scoped (to diff(..)), not block-scoped.
While you can declare var inside a block (and still have it be function-scoped), I would recommend against this approach except in a few specific cases. Otherwise, var should be reserved for use in the top-level scope of a function.
Why not just use let in that same location? Because var is visually distinct from let and therefore signals clearly, "this variable is function-scoped." Using let in the top-level scope, especially if not in the first few lines of a function, and when all the other declarations in blocks use let, does not visually draw attention to the difference with the function-scoped declaration.
In other words, I feel var better communicates function-scoped than let does, and let both communicates (and achieves!) block-scoping where var is insufficient. As long as your programs are going to need both function-scoped and block-scoped variables, the most sensible and readable approach is to use both var and let together, each for their own best purpose.
There are also other semantic and operational reasons to choose var or let in different scenarios.
WARNING:
My recommendation to use both var and let is clearly controversial and contradicts the majority. It's far more common to hear assertions like, "var is broken, let fixes it" and, "never use var, let is the replacement." Those opinions are valid, but they're merely opinions, just like mine. var is not factually broken or deprecated; it has worked since early JS and it will continue to work as long as JS is around.
Where To let?
My advice to reserve var for (mostly) only a top-level function scope means that most other declarations should use let. But you may still be wondering how to decide where each declaration in your program belongs?
POLE already guides you on those decisions, but let's make sure we explicitly state it. The way to decide is not based on which keyword you want to use. The way to decide is to ask, "What is the most minimal scope exposure that's sufficient for this variable?"
Once that is answered, you'll know if a variable belongs in a block scope or the function scope. If you decide initially that a variable should be block-scoped, and later realize it needs to be elevated to be function-scoped, then that dictates a change not only in the location of that variable's declaration, but also the declarator keyword used. The decision-making process really should proceed like that.
If a declaration belongs in a block scope, use let. If it belongs in the function scope, use var (again, just my opinion).
Why use var for function scoping? Because that's exactly what var does. There literally is no better tool for the job of function scoping a declaration than a declarator that has, for 25 years, done exactly that.
You could use let in this top-level scope, but it's not the best tool for that job. I also find that if you use let everywhere, then it's less obvious which declarations are designed to be localized and which ones are intended to be used throughout the function.
By contrast, I rarely use a var inside a block. That's what let is for. Use the best tool for the job. If you see a let, it tells you that you're dealing with a localized declaration. If you see var, it tells you that you're dealing with a function-wide declaration. Simple as that.
function getStudents(data) {
var studentRecords = [];
for (let record of data.records) {
let id = `student-${ record.id }`;
studentRecords.push({
id,
record.name
});
}
return studentRecords;
}
The studentRecords variable is intended for use across the whole function. var is the best declarator to tell the reader that. By contrast, record and id are intended for use only in the narrower scope of the loop iteration, so let is the best tool for that job.
In addition to this best tool semantic argument, var has a few other characteristics that, in certain limited circumstances, make it more powerful.
One example is when a loop is exclusively using a variable, but its conditional clause cannot see block-scoped declarations inside the iteration:
function commitAction() {
do {
let result = commit();
var done = result && result.code == 1;
} while (!done);
}
Here, result is clearly only used inside the block, so we use let. But done is a bit different. It's only useful for the loop, but the while clause cannot see let declarations that appear inside the loop. So we compromise and use var, so that done is hoisted to the outer scope where it can be seen.
The alternative—declaring done outside the loop—separates it from where it's first used, and either necessitates picking a default value to assign, or worse, leaving it unassigned and thus looking ambiguous to the reader. I think var inside the loop is preferable here.
There are other nuances and scenarios when var turns out to offer some assistance, but I'm not going to belabor the point any further. The takeaway is that var can be useful in our programs alongside let (and the occasional const). Are you willing to creatively use the tools the JS language provides to tell a richer story to your readers?
Don't just throw away a useful tool like var because someone shamed you into thinking it wasn't cool anymore. Don't avoid var because you got confused once years ago. Learn these tools and use them each for what they're best at.
Source: YDKJS by Kyle Simpson

Related

Difference between using higher scope variables and using variables explicitly passed in a function

I mainly code in JS, but I guess this would apply to many languages.
What is the effective difference between a global/higher scope variable in a function versus using a variable passed into the function, and vice versa?
let somevariable = 5 ;
function somefuntion() {
let scopedvariable = 10;
return scopedvariable*myvariable
}
somefunction();
// OR
let myvariable = 5 ;
function somefuntion(somevariable ) {
let scopedvariable = 10;
return scopedvariable *somevariable
}
somefunction(myvariable);
If a function uses a variable from scope that might cause a side effect(function modifying the outer variable) and this is considered bad practice because makes function impure.
Global variables considered bad practice and should only be used if variable is constant. If the variable is constant it is okay, because now function can't modify the scope.

Javascript - Arguments Vs Nested functions Vs Performance

Javascript noob question - which one of the following is best practice and performance friendly, also welcome any other suggestions too. This is simplest version of original project. There will be 20+ variables and 10+ inner functions (Ex: 'process' in the case).
The advantage of Method 1 is it doesn't need to send arguments to the functions but as it nests all the functions inside the main function (possibly regenerate all the inner function for each instance of Foo). While the method 2 is free from function nesting but requires lot of arguments. Note that also there will be multiple instance of Foo constructor.
Method 1:
function Foo () {
var a = 1;
var b = 2;
var c = (a+b)/2;
var $element = $('#myElement');
var process = function (){
var x = a+b+c;
$element.css('left', x)
return x;
}
x = process ();
}
Method 2:
function Foo () {
var a = 1;
var b = 2;
var c = (a+b)/2;
var $element = $('#myElement');
x = process (a, b, c, $element);
}
function process (a, b, c, $element){
$element.css('left', x)
return x;
}
The separated functions are definitely the way to go for performance. While there are situations where the variable scoping of nested functions would be nice to have, the performance hit can be huge if the function is large.
Keep in mind that every time Foo if called, var process is reallocating all the memory for the new function that is being created, rather than keeping the function in memory and just passing it new parameters. If the function is big, this is going to be a pretty intense task for the Javascript interpreter.
Here are some hard numbers that may help as well (Contains performance comparisons and actual benchmarks that you can run in your own browser): http://jsperf.com/nested-functions-speed, http://jsperf.com/nested-named-functions/5
Actually browsers do optimize even nested functions to where performance differences compared to functions declared in outer scope become negligible.
I blogged my findings and created a performance test to support this claim.
EDIT: The test was broken, after fixing the test shows that it's still faster not to nest functions.

Passing Vector.<T> references as Object, casting causes a copy to be made

I'd like to be able to pass Vectors around as references. Now, if a method takes a Vector.<Object>, then passing a Vector.<TRecord>, where TRecord inherits directly from Object does not work. Where a method takes just plain Object; say vec: Object, then passing the Vector is possible. Once inside this method, an explicit cast at some stage is required to access vec as a Vector again. Unfortunately, a cast seems to make a copy, which means wrapping one up in multiple Flex ListCollectionViews is useless; each ListCollectionView will be pointing to a different Vector.
Using Arrays with ArrayCollection presents no such problem, but I lose out out the type safety, neatness (code should be clean enough to eat off of) and performance advantages of Vector.
Is there a way to cast them or pass them as references in a generic manner without copies being made along the way?
Note in this example, IRecord is an interface with {r/w id: int & name: String} properties, but it could be a class, say TRecord { id: int; name: String} or any other usable type.
protected function check(srcVec: Object): void
{
if (!srcVec) {
trace("srcVec is null!");
return;
}
// srcVec = (#b347e21)
trace(srcVec.length); // 4, as expected
var refVec: Vector.<Object> = Vector.<Object>(srcVec);
// refVec = (#bc781f1)
trace(refVec.length); // 4, ok, but refVec has a different address than srcVec
refVec.pop();
trace(refVec.length); // 3 ok
trace(srcVec.length); // 4 - A copy was clearly created!!!
}
protected function test(): void
{
var vt1: Vector.<IRecord> = new Vector.<IRecord>; // (#b347e21) - original Vector address
var vt2: Vector.<Object> = Vector.<Object>(vt1); // (#bbb57c1) - wrong
var vt3: Vector.<Object> = vt1 as Vector.<Object>; // (#null) - failure to cast
var vt4: Object = vt1; // (#b347e21) - good
for (var ix: int = 0; ix < 4; ix++)
vt1.push(new TRecord);
if (vt1) trace(vt1.length); // 4, as expected
if (vt2) trace(vt2.length); // 0
if (vt3) trace(vt3.length); // vt3 is null
if (vt4) trace(vt4.length); // 4
if (vt1) trace(Vector.<Object>(vt1).length); //
trace("calling check(vt1)");
check(vt1);
}
This is not possible. If a type T is covariant with type U, then any container of T is not covariant with a container of type U. C# and Java did this with the built-in array types, and their designers wish they could go back and cut it out.
Consider, if this code was legal
var vt1: Vector.<IRecord> = new Vector.<IRecord>;
var vt3: Vector.<Object> = vt1 as Vector.<Object>;
Now we have a Vector.<Object>. But wait- if we have a container of Objects, then surely we can stick an Object in it- right?
vt3.push(new Object());
But wait- because it's actually an instance of Vector.<IRecord>, you can't do this, even though the contract of Vector.<Object> clearly says that you can insert Object. That's why this behaviour is explicitly not allowable.
Edit: Of course, your framework may allow for it to become a non-mutable reference to such, which is safe. But I have little experience with ActionScript and cannot verify that it actually does.

For-loop variable scope confusion

I have noticed a weird behavior of the variables in for loops. It's not really a problem, but it disturbs me a lot.
Actually I've created two loops this way:
for (var i:uint; i<19; i++) SomeFunction (i);
for (var i:uint; i<26; i++) SomeOtherFunction (i);
What I received was a compilation warning:
Warning: Duplicate variable definition.
This warning really surprised me. Nothing like that ever happened to me in other languages.
It seems that the i variable gets into the scope that is higher in the hierarchy and becomes available out of the loop's block. I've also tried to embrace the loop block in a curly brace, but it didn't change anything.
Why does it happen? Is it normal? Is it possible to avoid it? For now I've just set different names for both of the variables, but that's not a real solution I think. I'd really like to use the i-named variable in most of my for-loops.
yes, the loop increment variable is in the scope of the loops parent, not inside the loop itself. This is intentional, for examples like this:
public function getPositionOfValue ( value:String ) : int
{
for ( var i:int = 0; i < someArray; i++ )
{
if (someArray[i] == value )
{
break;
}
}
return i;
}
this allows you to access the value of i once the loop is over. There are lots of cases where this is very useful.
What you should do in the cases where you have multiple loops inside the same scope is var the i outside of the loops:
public function getPositionOfValue ( value:String ) : int
{
var i:int;
for ( i = 0; i < 15; i++ )
{
//do something
}
for ( i = 0; i < 29; i++ )
{
//do something else
}
return i;
}
then you get rid of your warning. The other thing to consider is to name your loop increment variables something more descriptive.
Update: Two other things to consider:
1) you shouldn't use uints except for things like colors and places where Flex expects a uint. They are slower than int's to use. Source]1 Update: it looks like this may no longer be the case in newer versions of the flash player: source
2) when you var a loop increment variable inside of a loop declaration, you want to make sure you set it to the proper initialization value, usually 0. You can get some hard to track down bugs if you dont.
As mentioned here, as3 has global and local scope and that's about it.
It does not do block-level scoping (or for-level either). With hoisting, you can even write to variables before you define them. That's the bit that would do my head in :-)
Early versions of Visual C had this bug, leading to all sorts of wonderful funky macro workarounds but this is not a bug in as3, it's working as designed. You can either restrict your code to having the declaration in the first for only or move the declaration outside all the for statements.
Either way, it's a matter of accepting that the language works one way, even though you may think that's a bad way :-)
Declare the variable i outside the loops to avoid this. As long as you reset it (i=0) you can still use it in all loops.
var i : uint;
for (i=0; i<19; i++) SomeFunction(i);
for (i=0; i<26; i++) SomeOtherFunction(i);

Segfault Copy Constructor

My code is as follows:
void Scene::copy(Scene const & source)
{
maxnum=source.maxnum;
imagelist = new Image*[maxnum];
for(int i=0; i<maxnum; i++)
{
if(source.imagelist[i] != NULL)
{
imagelist[i] = new Image;
imagelist[i]->xcoord = source.imagelist[i]->xcoord;
imagelist[i]->ycoord = source.imagelist[i]->ycoord;
(*imagelist[i])=(*source.imagelist[i]);
}
else
{
imagelist[i] = NULL;
}
}
}
A little background: The Scene class has a private int called maxnum and an dynamically allocated Array of Image pointers upon construction. These pointers point to images. The copy constructor attempts to make a deep copy of all of the images in the array. Somehow I'm getting a Segfault, but I don't see how I would be accessing an array out of bounds.
Anyone see something wrong?
I'm new to C++, so its probably something obvious.
Thanks,
I would suggest that maxnum (and maybe imagelist) become a private data member and implement const getMaxnum() and setMaxnum() methods. But I doubt that is the cause of any segfault the way you described this.
I would try removing that const before your reference and implement const public methods to extract data. It probably compiles since it is just a reference. Also, I would try switching to a pointer instead of pass by reference.
Alternatively, you can create a separate Scene class object and pass the Image type data as an array pointer. And I don't think you can declare Image *imagelist[value];.
void Scene::copy(Image *sourceimagelist, int sourcemaxnum) {
maxnum=sourcemaxnum;
imagelist=new Image[maxnum];
//...
imagelist[i].xcoord = sourceimagelist[i].xcoord;
imagelist[i].ycoord = sourceimagelist[i].ycoord;
//...
}
//...
Scene a,b;
//...
b.Copy(a.imagelist,a.maxnum);
If the source Image had maxnum set higher than the actual number of items in its imagelist, then the loop would run past the end of the source.imagelist array. Maybe maxnum is getting initialized to the value one while the array starts out empty (or maxnum might not be getting initalized at all), or maybe if you have a Scene::remove_image() function, it might have removed an imagelist entry without decrementing maxnum. I'd suggest using an std::vector rather than a raw array. The vector will keep track of its own size, so your for loop would be:
for(int i=0; i<source.imagelist.size(); i++)
and it would only access as many items as the source vector held. Another possible explanation for the crash is that one of your pointers in source.imagelist belongs to an Image that was deleted, but the pointer was never set to NULL and is now a dangling pointer.
delete source.imagelist[4];
...
... // If source.imagelist[4] wasn't set to NULL or removed from the array,
... // then we'll have trouble later.
...
for(int i=0; i<maxnum; i++)
{
if (source.imagelist[i] != NULL) // This evaluates to true even when i == 4
{
// When i == 4, we're reading the xcoord member from an Image
// object that no longer exists.
imagelist[i]->xcoord = source.imagelist[i]->xcoord;
That last line will access memory that it shouldn't. Maybe the object still happens to exist in memory because it hasn't gotten overwritten yet, or maybe it has been overwritten and you'll retrieve an invalid xcoord value. If you're lucky, though, then your program will simply crash. If you're dealing directly with new and delete, make sure that you set a pointer to NULL after you delete it so that you don't have a dangling pointer. That doesn't prevent this problem if you're holding a copy of the pointer somewhere, though, in which case the second copy isn't going to get set to NULL when you delete-and-NULL the first copy. If you later try to access the second copy of the pointer, you'll have no way of knowing that it's no longer pointing to a valid object.
It's much safer to use a smart pointer class and let that deal with memory management for you. There's a smart pointer in the standard C++ library called std::auto_ptr, but it has strange semantics and can't be used in C++ containers, such as std::vector. If you have the Boost libraries installed, though, then I'd suggest replacing your raw pointers with a boost::shared_ptr.