In javascript should I use const instead of var whenever possible? - javascript

If creating a reference to an object, and the reference is not going to change (even though the object will), is it better to use const instead of var?
For example:
const moment = require('moment')
exports.getQuotation = function(value) {
const quotation = {};
quotation.value = value;
quotation.expiryDate = moment().add(7, 'days');
// Do some other stuff with quotation perhaps
return quotation;
};

You can use const, but you have to run node on --harmony
node app.js --harmony
You also have to set "use strict", otherwise you'll have run-time execution issues:
exports.getQuotation = function(value) {
"use strict";
const I_AM_CONSTANT = {};
let quotation = {};
...
Other than that, yes if you are running node on --harmony, semantically it makes more sense to use const for constants. For actual variables you should use let instead of var, as let only defines the variable in the scope (avoids hoisting issues)

If you check MDN, for its reference you can find that its an experimental technology which is part of ES6 proposal. Its browser support is limited.
Reference for const from MDN
This is an experimental technology, part of the Harmony (ECMAScript 6)
proposal. Because this technology's specification has not stabilized,
check the compatibility table for usage in various browsers. Also note
that the syntax and behavior of an experimental technology is subject
to change in future version of browsers as the spec changes.
So this answers whether you should use everywhere. :)
Also if you want to create properties in objects which should not be allowed to change, then you should check Object.defineProperties() and the use of writable attribute.

const is used for a variable constant, i.e. once declared, it's value is not supposed to change, whereas var can be changed as per the will.
Use of const and var is solely on your will. For me it goes as follows
If I'm 100% sure that the value of a variable is not going to change and it's not going to re-declared, I'll define it as a const.
In all other cases I'll use var.
Mind it, re-declaring const will immediately throw an error.

Related

Why is the variable `closed` being logged as `false`, if I define it globally as `0`?

I know this must be really basic stuff but I don’t understand how the scope is working. I want the closed variable be known in the entire JavaScript file.
I have something like that (in jQuery):
var closed = 0;
$(function(){
console.log(closed);
});
But closed is getting logged as false. I tried many things with load and onload functions, but I failed.
The problem: global read-only properties
First, note that this problem exists in a Web environment, i.e. in a browser.
Other JS environments might not have this problem.
var closed = 0; in the global scope doesn’t create a binding that refers to a number and remains being the boolean false.
The reason for this is that closed refers to window.closed in this circumstance; it’s always a boolean, and redefining it produces a linter warning like “Redefinition of closed”.
This read-only property indicates whether the referenced window is closed or not.
Variable names that behave like this can be found in these lists:
Window properties
WorkerGlobalScope properties
There also used to be documentation on MDN about a mixin called WindowOrWorkerGlobalScope, whose properties would fit into this list; however, this just refers to things that are both in Window and in WorkerGlobalScope.
Tiny piece of context: MDN did a refactoring of their documentation where they removed all these “mixins” in favor of standard interface names.
You can find archived versions of this list.
Window or WorkerGlobalScope are two of the interfaces that globalThis can be an instance of in Web environments.
Run the snippet to get a full list of variable names that you can’t safely use in the global scope:
const props = Object.entries(Object.getOwnPropertyDescriptors(globalThis)),
undesirable = {
set: (desc) => desc,
configurable: (desc) => !desc,
writable: (desc) => !desc
};
Array.from(document.querySelectorAll("[id]"))
.forEach((span) => span.innerHTML = props
.filter(([prop, {[span.id]: desc}]) => undesirable[span.id](desc))
.map(([prop]) => `<code>${prop}</code>`)
.join(", "))
code{
background: #eee;
padding: 1px 3px;
}
<p>Properties that have a setter which may change the type or invoke some function, when a value is set to it:</p>
<span id="set"></span>
<hr/>
<p>Properties that are not configurable:</p>
<span id="configurable"></span>
<hr/>
<p>Properties that are read-only:</p>
<span id="writable"></span>
You’ll notice, it’s a lot.
It’s also a lot of short, common variable names like name, length [1], [2], status [1], [2], self, top, menubar, and parent.
Furthermore, regarding the invocation of some function when assigning to a setter, something like var location = "Podunk, USA"; actually redirects you to the location ./Podunk, USA.
There’s a related issue of using function names like lang, checked, autocomplete, evaluate, or animate in event attributes like onclick: there, not only the window properties are included in the scope chain, but also all scopable properties of the entire prototype chain of the current HTMLDocument and of the current specific Element (e.g. using <a onclick=""></a> provides access to everything starting at HTMLAnchorElement.prototype) and perhaps some more, e.g. anything from the form property (of form elements) if it exists.
All this is also explained in this answer to a related question.
This problem is also exhibited when relying on DOM nodes with an id to become global properties.
Solutions
There is a handful of solutions to this.
I’ll list them in a subjective order of best to worst.
Use modules instead
Running your JavaScript code as modules instead of scripts not only is the new cool thing to do, it also just doesn’t have this problem.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your site</site>
<script type="module" src="main.mjs"></script>
</head>
<body>
<!-- Content. -->
</body>
</html>
main.mjs:
const top = { spinning: true },
window = { type: "clear", frame: "wood" };
let document = { type: "LaTeX", content: "\\documentclass[a4]{…} …" },
closed = 0;
var location = "Podunk, USA";
// All of these are the variables defined above!
console.log({ top, window, document, closed, location });
Modules also come with a few other helpful features like implicit deferment in relation to DOM parsing.
Wrap everything in a new scope
In a script’s global scope, you will encounter this problem.
In a new function scope, you don’t have this problem:
(function(){
const closed = 0; // Or `let` or `var`.
$(function(){
console.log(closed);
});
})();
You can also create a more useful scope, for example jQuery’s $(function(){…}); which waits for the DOM to be loaded.
This, by the way, is probably the option with the highest compatibility while still being one of the better options.
Since I don’t use jQuery, I prefer to wrap everything in a DOMContentLoaded listener where I can scope all the variables I need, and, in addition, use "use strict";:
addEventListener("DOMContentLoaded", () => { "use strict";
// Code goes here.
});
This avoids the clash between global properties and global variables.
Always use const. If you can’t use const, use let. If you can’t use let, use var.
… But you’ll never need var!1
Using const or let in scripts, as Ankit Agarwal’s answer suggests, mitigates this problem:
const closed = 0;
console.log(closed); // 0
However, this only works for most variable names, but not all — const closed = 123; let history = "Hello, world!"; all work, but const window = 123;, const top = 123; or let document; don’t.
1: Although there may be one edge-case.
And obviously, older browsers still need var.
Just use different variable names
Sounds obvious, but this isn’t very robust.
New global properties may always pop up, so global variables may clash with them at any time.
Since this would obviously break several scripts all over the Internet, global properties which are added nowadays don’t really create much of an issue.
const history = "Regency Era"; works just fine and makes history usable as a variable that refers to this string.
var history =…; still doesn’t, and it throws an error in strict mode, and fails silently in loose mode.
This has historically led to compatibility issues which is why this isn’t a robust option, especially when using var, which in turn is another strong argument against var.
Reassigning older properties, such as document, will fail in scripts, in certain environments, but every variable name will work in a module, in every environment, which is why this is the top recommended option.
Use let instead of var as closed is a global variable used by JavaScript runtime so to make it local to the scope of the code you can use let instead of var which set the variable to global scope considering the global closed property.
let closed=0;
$( function() {
console.log(closed);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

How to "take variables out of an object" in javascript

I'm looking for something that will import the contents of an object to the global scope:
var y = {}
y.x = 5
//do some magic here
console.log(x); //5
I want to do this is so I can make an easy to use module with memorable function names without having to worry about things accidentally getting overridden by other modules.
Consider this example:
funModule = {};
funModule.washClothes = function(clothes){...}
funModule.walkDog = function(dogName){...}
//etc
funModule.UNITED_STATES_DEFICIT = ...;
Here I've created a module that has some useful functions and constants (implementations and values were replaced with "...").
I don't want my users to have to type out the module name every time they call function or use a constant. That would result with really messy code:
funModule.walkDog(funModule.UNITED_STATES_DEFICIT);
I could try it again by defining everything globally:
washClothes = function(clothes){...}
walkDog = function(dogName){...}
//etc
UNITED_STATES_DEFICIT = ...;
but if a different module has also defined the commonly named function washClothes we've got trouble. (in my actual case the commonly named function is run)
Removed from technical context, here is the problem I'm faced with:
Firstly I want to use simple and memorable names to make the module easy to learn and fun to use.
Secondly I don't want the easy names to make the module impossible to use with others. Especially as it grows, a lot of common names will be used. It would be great if the users could decide whether or not import the names directly.
Thirdly I realized as I'm typing this that what I'm describing is something that definitely already exists, in python. See http://effbot.org/zone/import-confusion.htm for more detail.
tl;dr How can python-like imports be done with javascript?
EDIT:
It seems there is not a universal way to do this.
Using Window won't work in all environments (but will work in any common browser).
Apparently ES6 Modules are not available to web browsers directly.
This question is different from this one because its not about Node.js. I was looking for a universal way to do it, but that doesn't seem possible, so I'll limit it to web browsers, (namely chrome, firefox, safari, opera, and maybe ie)
EDIT:
This general article about Scope could be useful for anyone with a similar question as mine: https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/
Object.prototype.makeglobal=function(){
for(key in this){
if(window[key]){//replace window if youre not in a browser
//already exist, error handling
console.error(key+' already exist in window');
}else{
window[key]=this[key];
}}};
Use like this:
funModule.makeglobal();
//now you can
washClothes();
But this is bad as it pollutes the global object.
2.Your user should create his own namespace:
function(){
this.washClothes();
//more of his content
}.call(funModule);
3.You could also add a loader:
funModule.load=function(func){
console.log(func);
console.log(this);
func.call(this,this);
};
Now you can do:
funModule.load(function(fun){
this.washClothes();
fun.washClothes();
});
4.If youre concerned about readability you may use function chaining (?):
funModule.washClothes=function(){
//your code
return this;
}
now you can do:
funModule.washClothes("tshirts").washClothes("trousers").washClothes();
ES6 Modules are what you want.
If you will define your object as es6 module you could do this (using the names in your example):
import { washClothes } from "fun-module";
and then washClothes will be globally available on the file that imported it, just like you want.
Read about it here.
If you really want a magic solution like in the comment in your post and don't want to use ES6 and you run in the browser you can put it on the window object:
window.x = 5
In JavaScript, at least in a browser, global variables are properties of the window object: that is, window.x and x (where x is global) reference the same value. So, in theory, you could use Object.assign() to copy your object's properties to the window object making them global variables. This is roughly equivalent to globals().update(myobj.__dict__) in Python.
But just as import * is usually a bad idea in Python, so too this sounds like a bad idea, except even worse because window has a lot of other properties that you probably don't want to clobber.
After some additional research I found a way, without polluting the global namespace, to allow users to directly access module contents.
This solution allows the user to:
Write code that directly references the module's functions/properties
Define precedence if there are multiple modules written in this same style
Still access the module's functions/properties by module name*
*This feature comes with a catch
Here's the code
Module
funModule = {};
//This stuff is the arbitrary contents of the module:
funModule.washClothes = function(clothes){...}
funModule.walkDog = function(dogName){...}
//etc
funModule.UNITED_STATES_DEFICIT = ...;
//etc
//This part is necessary:
funModule.run(userApp)
{
for(key in this){
eval(key + " = " + this[key] + ";");
}
userApp();
}
The only way (that I could find) to dynamically define functions both in funModule.run's scope and in funModule is to use Eval. Using call, apply, or bind to manipulate scope would still require use of the this keyword and the whole point of this unusual style is to make client code as simple and non-repetitive as possible.
Client Code 1
function myApp()
{
washClothes(UNITED_STATES_DEFICIT);
}
funModule.run(myApp);
Here in the client code it is possible to directly access everything except for funModule.run. So the global namespace is kept clean but the user's code does not need unnecessary repetition.
Client Code 2
function myApp()
{
washClothes(UNITED_STATES_DEFICIT);
}
funModule.run( otherModule.run.bind({},myApp) ); //otherModule has precedence here
Assume otherModule is a different module that features the same run function. funModule will load its contents then call its first argument. The first argument will load otherModule's contents, overriding anything from funModule with the same name.
Client Code 3
function myApp()
{
//directly access stuff from funModule
walkDog()
var big = UNITED_STATES_DEFICIT * 3.14;
//explicitly access stuff from specific modules
clothes = new otherModule.Clothes();
funModule.washClothes(otherModule.washClothes(clothes));
}
funModule.run(myApp)
This is the feature that makes use of eval necessary. The user can opt out of ambiguity of direct access. They can still access properties/methods by naming the module they come from.
But Why?
Some StackOverflow users were understandably concerned about the unusual set of constraints in the question, so I figured I would answer the following question:
Why don't you use a short alias for your module.
I tried to answer that question in this article, which pulls from this question and answer.

How to alias pseudo-namespaced code using let keyword?

As a beginner in JS, I would like to understand one usage of let declaration, as shown below from MDN,
You can use let definitions to alias pseudo-namespaced code in extensions. (See Security best practices in extensions.)
let Cc = Components.classes, Ci = Components.interfaces;
What is pseudo-namespaced code?
How to alias pseudo-namespaced code using letkeyword?
When dealing with namespaces in Javascript, it can become quickly annoying to always specify the full namespace to access a value or method. For example:
let myModule = {
mySubModule: {
mySubSubModule: {
myMethod: function () {}
}
}
};
// to use myMethod, we need to write the full namespace
myModule.mySubModule.mySubSubModule.myMethod();
By storing the "path" in a variable, we can avoid writing the full namespace each time we need to call myMethod:
let shortcut = myModule.mySubModule.mySubSubModule;
shortcut.myMethod();
I don't really understand why there is a paragraph specifically for Firefox extensions. As long as your browser understands the let keyword of the Ecmascript 6 specification, you are free to use this technique with whatever namespace you want. It also works with the var keyword, although you won't benefit from features like block-scoping.

Why is it impossible to assign constant properties of JavaScript objects using the const keyword?

First, I had asked is it possible: How to create Javascript constants as properties of objects using const keyword?
Now, I gotta ask: why? The answer to me seems to be 'just because', but it would be so useful to do something like this:
var App = {}; // want to be able to extend
const App.goldenRatio= 1.6180339887 // throws Exception
Why can constants set on an activation object work but not when set on they are set on any other?
What sort of damage can be done if this were possible?
What is the purpose of const, if not to prevent a public API from being altered?
If you want an unchangeable value in a modern browser, use defineProperty to create an unwritable (aka read-only) property:
var App = {};
Object.defineProperty(App,'goldenRatio',{value:1.6180339887});
console.log( App.goldenRatio ); // 1.6180339887
App.goldenRatio = 42;
console.log( App.goldenRatio ); // 1.6180339887
delete App.goldenRatio; // false
console.log( App.goldenRatio ); // 1.6180339887
If you don't pass writable:true in the options to defineProperty it defaults to false, and thus silently ignores any changes you attempt to the property. Further, if you don't pass configurable:true then it defaults to false and you may not delete the property.
Apart from the fact that const is and not supported cross-browser (it's an ES6 feature), const App.goldenRatio= 1.6180339887 doesn't work for the same reason var App.goldenRatio= 1.6180339887 doesn't: you're setting an object property, so it's a syntax error to prepend it with a var or const keyword.
Going to answer the question you meant to ask and say never use const. The interpreter doesn't do anything with it. All it does is mislead the developer into thinking s/he can assume that the value never changes, which is about as likely as if the const keyword weren't present.
var MY_CONSTANT = "some-value";
You can use conventions like ALL_CAPS to show that certain values should not be modified

Is there a way to define symbolic constants in Javascript?

I searched if JavaScript offers a mean to define symbolic constants, but didn't find anything. Did I miss something ?
Is it a common practices to use const var instead ?
var const MAXIMUM_VALUE = 100;
Thanx.
const is not supported by IE, so if you want to support IE that is out of the question.
As far as I know, and the best way of doing this to keep it simple is to just have a naming convention for your constants like the ever-popular ALL UPPERCASE. There are some examples out there to force constants but they are not worth it for the most part. Alternatively, you could use a function:
function myConst() { return 'myValue'; }
This can of course still be overridden but I've seen it used.
Also see:
Are there constants in Javascript?
Is it possible to simulate constants in Javascript using closures?
Javascript: final / immutable global variables?
Yes. But you remove the var. const replaces var.
const MAXIMUM_VALUE = 100;
Object.defineProperty(window, 'CONSTANT_NAME', {value: CONSTANT_VALUE});
// usage
console.log(CONSTANT_NAME);
Object.defineProperty() creates a property with the following default attributes:
configurable true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable true if and only if this property shows up during enumeration of the properties on the corresponding object. Defaults to false.
writable true if and only if the value associated with the property may be changed with an assignment operator. Defaults to false.
if the "constant" is an object you might additionally want to make it immutable by freezing it. obj =Object.freeze(obj). have in mind that child-property-objects are not automatically frozen.

Categories