Does the const keyword in JavaScript create an immutable reference to immutable data structures? [I'm assuming that immutable data structures exist in JavaScript.]
For string it appears to do so:
var x = "asdf";
const constantX = x;
alert("before mutation: " + constantX);
x = "mutated"
alert("after mutation: " + constantX);
output:
before mutation: asdf
after mutation: asdf
http://jsfiddle.net/hVJ2a/
first you aren't mutating the string, you're reassigning the reference.
You're right that const isn't available in all common browsers. But even if it were, it is not sufficient. const will prevent the reference from being reassigned - but if you have a const reference to a mutable object, you haven't accomplished very much.
const var o = { foo: 'bar' };
o = { foo: 'baz'}; // throws
o.foo = 'baz'; // allowed
So that brings us to your question, does js even have immutable data structures? No, js does not come with them. Immutable datastructures can be coded as a library - and assignment is not even defined, so o.foo = 'baz' doesn't even make sense. You have to write it as const var o2 = o.assoc('foo', 'baz') which will return a brand new object o2 instead of mutating o
But immutable data structures as a library doesn't mean much if nobody uses them. E.g. if angular uses regular javascript datastructures, you have to use them too. Otherwise you'd have to convert between mutable and immutable at the boundary between your code and angular.
opinion follows:
IMO your only practical options for doing real functional programming for production browser apps is to wait around for something like ClojureScript to mature. ClojureScript reboots the library ecosystem, so as libraries get written, they use immutable datastructures by default.
You can of course do half-baked functional programming in javascript using tools like underscore.js and Facebook React, which is what I do for the production webapps I build. But you have to establish immutability by convention, and people are going to mutate things on accident or because they don't know any better and you have to deal with those challenges.
Wrong. It is not immutable, from the MDN Documentation for const:
The const declaration creates a read-only reference to a value. It
does not mean the value it holds is immutable, just that the variable
identifier cannot be reassigned.
Yes, it does create an immutable reference, but it is not been standardized and is not supported in all browsers.
See this MDN article on the const keyword for details and compatability.
However, it doesn't do anything to set the referenced data structure as immutable. Several answers below address that question using, for example, Object.freeze
The only immutable data structure (something that is allocated on heap) is string. All other object alike things like objects, arrays and functions are mutable by default.
The const creates immutable variable. Yes, "const variable" sounds controversial but that is closest term I can come up with for JS.
The const content cannot be changed outside of its declaration. But it may contain reference, for example, to the object that is mutable by itself. So you can modify its properties through that const reference to it.
And if you want to make object to be immutable then there is a Object.freeze(obj) method.
You can create CONST like values using ES5 Object.defineProperty. The crummy part is that it must be bound to an object
CONSTS = {};
Object.defineProperty(CONSTS, 'FOO', {
value: 'bar'
});
CONSTS.FOO = 'derp' // Will throw error in strict mode
delete CONSTS.FOO // will throw error in strict mode
Yes that is right. A const would only declare a read-only named constant. Changing its value will have no effect.
Reference for const on MDN:
Const creates a constant that can be global or local to the function in which it is declared. Constants follow the same scope rules as variables.
The value of a constant cannot change through re-assignment, and a constant cannot be re-declared. Because of this, although it is possible to declare a constant without initializing it, it would be useless to do so.
A constant cannot share its name with a function or a variable in the same scope.
Here's the browser compatibility matrix.
const is a proposed feature of ECMAScript(together with a properly block-scoped let it is supposed to replace var and implicit global). ECMAScript Harmony is a grab-bag of ideas for the next versions of ECMAScript.
If you looking for read only variable, you can do like this
var constants = new (function() {
var x = 200;
this.getX = function() { return x; };
})();
You can use like this
constants.getX()
For example:
const basket = [1,2,3];
//Even though the variable is declared as const this one works
basket[0] = 1111;
console.log(basket);
//This one throws an error
basket = "Other primitive type."
Related
Please consider this snippet:
let variableName = 'internalVariable';
{
let internalVariable = 'whatever';
console.log(eval(propertyKey)); // prints 'whatever'
}
What are my options to accessing internalVariable via the string stored in variableName? I was hoping for something like scope[variableName], but there seems to be nothing like it.
Given this particular scenario, are there any alternatives to using eval?
In situations where dynamic access to something like a variable is required, the idiomatic thing to do is employ an object and dynamically compute property names as appropriate.
var obj = {};
obj[getPropertyName()] = "hello world";
Variables declared in functions with var, let, or const do exist as properties of something like an object (the closure of a function call), but JavaScript does not provide any way of referring to that thing as an object. Using eval() is possible, but generally it's a bad idea because runtime optimization is not attempted in modern runtime systems because eval() makes that intractably complicated.
The only way a variable is aliased in JavaScript is via the arguments object, and that's sufficiently weird that it's explicitly discouraged in "strict" mode.
[edit] — in re: Bergi's comment below, the (generally deprecated) with statement allows implicit references to object properties, and the export mechanism for modules can create aliases, though to me it's hard to imagine that being a good thing in actual practice.
In Javascript, local variables do not live on any object that I'm aware of. That is,
function foo() {
const x = 2;
self.x; // undefined
this.x; // undefined
window.x; // undefined
x; // 2, obviously
eval('x'); // 2
}
The last option eval('x') shows that it is possible to refer to these variables by name. I'm looking to extend this and access a variable using a name pattern:
function foo() {
// Code not under my direct control
function foobar_abc() {}
function other_functions() {}
// Code under my control
const matchingFunction = // find the function with name matching 'foobar_*'
}
If this lived on an object, I would use something like myObject[Object.keys(myObject).find((key) => key.startsWith('foobar_'))]. If it were in the global scope, myObject would be window and everything works.
The fact that eval is able to access the variable by name implies that the value is available somewhere. Is there any way to find this variable? Or must I resort to techniques which re-write the (potentially very complex) code which is not under my direct control?
I'm targeting modern browsers, and in this use case I don't mind using eval or similarly hacky solutions. Arbitrary code is already being executed, because the execution of user-provided code is the purpose.
Another option is to use code parsing to deduce the function names using a javascript AST (abstract syntax tree) library. The "esprima" package will probably be good place to look:
https://www.npmjs.com/package/esprima
So you can do
import esprima from 'esprima'
const userCodeStructure = esprima.parseScript( userProvidedJavascriptString );
const allTopLevelFunctionDeclarations = userCodeStructure.body.filter( declaration => declaration.type === "FunctionDeclaration" );
const allTopLevelFunctionNames = allTopLevelFunctionDeclarations.map( d => d.id );
I haven't tried this myself by the documentation suggests it should work (or something like it):
http://esprima.readthedocs.io/en/latest/
One possible approach that might help you here is to evaluate at global scope rather than in a function, and that might put the functions on the window object instead of in local scope.
Easiest way to do this is probably to write a new tag into the document and inject the user-provided code.
Relying on variable names is the wrong approach.
eval is evil. It may not be available under CSP. Considering that the code is supposed to run in browser, the biggest problem is that variables don't have expected names in minified code. They are a, b, c...
In order to maintain their names, they should be object properties - and so they will be available on the object.
Or must I resort to techniques which re-write the (potentially very complex) code
This is refactoring and that's what should be done to avoid bad code that smells and creates major problems.
Is it true that in JavaScript ES6, you can
const foo = {};
and then still change the object in anyway you want?
Such as
foo.bar = 1;
foo.bar++;
delete foo.bar;
because it is a "constant" (const) meaning that the reference to this object can never change, but what is inside of this object (all the properties or methods (which are also properties)) can change in any way?
The const declaration creates a read-only reference to a value. It
does not mean the value it holds is immutable, just that the variable
identifier cannot be reassigned. For instance, in case the content is
an object, this means the object itself can still be altered.
More on Const at MDN
ES6 const does not indicate that a value is ‘constant’ or immutable. A const value can definitely change. The only thing that’s immutable here is the binding.
Yes, in ES6 you can change the Object properties and assign any value you want even if you are using
const
const at MDN
Is there an elegant solution to destructure an object without specifying all the object's properties?
I wondered if it was possible to use the spread operator but it seems like that's not possible because objects are not arrays!
I guess it may be considered a bad idea to blindly declare variables but I think this would be useful for very large objects.
This is what the with(){} construction allowed:
var obj = {a: 1};
with (obj) {
console.log(a);
}
This construct is however severely discouraged and basically deprecated (it throws an error in strict mode), because it has some major drawbacks:
Your code is hard to read, because it's impossible to tell apart 1) Variables from outer scopes 2) Local variables 3) Properties coming from the object.
Your code can't be optimized, because the JavaScript engine can't tell where your variables are coming from.
Your code is much harder to refactor, because if you introduce a property to your obj, it might shadow some existing local variable, exemple:
var obj = {};
var a = 1;
addSomeProperties(obj);
with (obj) {
console.log(a); // the result here depends on whether or not "addSomeProperties" puts a property named "a" on "obj"
}
TL;DR version: you really don't want this, it makes your code brittle, hard to read&refactor. Just pick the pieces you want apart using the "normal" destructuring syntax.
I read this post, using delete keyword, we can delete JavaScript variable. But when I tried the same operations with constant but it is returning false when I try to delete constant. Is there any way to delete constants from memory?
I tried this answer but its also not working.
You can't directly do it, looking at the specs show us that the value can be set, but not over-written (such is the standard definition of a constant), however there are a couple of somewhat hacky ways of unsetting constant values.
Using scope
const is scoped. By defining the constant in a block it will only exist for this block.
Setting an object and unsetting keys
By defining const obj = { /* keys */ } we define a value obj that is constant, but we can still treat the keys like any other variable, as is demonstrated by the examples in the MDN article. One could unset a key by setting it to null.
If it's memory management that is the concern then both these techniques will help.
The delete operator is actually for deleting an object property, not a variable. In fact, in strict mode, delete foo is a syntax error.
Usually you can "delete" a value/object by removing all references to it, e.g. assigning null to a variable.
However, since constants are not writable (by definition) there is no way to do this.
As I wrote on my comment, delete can only be used on objects and arrays. So, what you can actually do is store all your constants in a constant object and free up memory by deleting it's properties, like this:
const myConstants = {};
myConstants.height = 100;
delete myConstants.height;
Would this piece of code work or is it counterproductive?
export default function freeObject(object) {
const keys = Object.keys(object)
for (const key of keys) {
delete object[key]
}
}