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.
Related
Can I safely assume that in any implementation of JavaScript,
1.) for any object generated by the var obj = { ... }; construct, obj[s] is undefined for any string s unless obj[s] has been explicitly set by my own code?
2.) if typeof obj === 'object' (and obj does not stem from some global, pre-defined variable or function in the global namespace), Object.hasOwnProperty(obj, s) is false for any string s except when I have set property s explicity before or, maybe, when Array.isArray(obj) is true?
In short: Can I assume that user-generated objects that are neither arrays nor of function type do not have pre-defined own properties?
Background: I need to write an interpreter for a very tiny subset of JavaScript that should safely execute user code. I would like to leverage on the optimization capabilities of the JavaScript engine. Hence I am planning to (1.) parse the user's code, (2.) re-write the AST such that (a) no global names can be accessed, (b) property access is restricted by a construct like ((typeof obj === 'object') && Object.hasOwnProperty(obj, s)) ? obj[s] : undefined, (3.) eval the re-written code. For this to work, it is necessary for the objects not to have predefined properties like, e.g., (function () {}).caller, as otherwise the user could make my interpreter to execute arbitrary code or mess with the global objects of my environment in general.
Does, maybe, anybody know of a package where something like this has been done already? My requirements are not high: I need to execute user code, the user needs to work with numbers, strings, arrays, objects, and functions, and I need to exchange these things with the user code.
Edit: First assumption is wrong, see answers.
You can't assume #1. obj[s] will access inherited properties, not just own properties.
var obj = {a: 1, b: 2}
console.log(obj["__proto__"] === undefined);
console.log(obj["toString"] === undefined);
I think #2 is a safe assumption. The whole point of hasOwnProperty() is to distinguish inherited properties from properties that were assigned explicitly in the object.
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.
var completeObj = {a: { b: { c: { d: { e: { f: 23 } } } } } };
var funcA = function(obj){
var a = 'a',b='b',c='c',d='d',e='e',f='f';
return obj[a][b][c][d][e][f];
}
var funcB = function(obj){
return obj['a']['b']['c']['d']['e']['f'];
}
funcA is much slower than funcB,looking for varible in scope cost so much time?
test url : http://jsperf.com/static-and-dynamic-argument
thx
http://jsperf.com/static-and-dynamic-argument/2
I took your test cases and added one to it to 'prove a point'. When you access somthing in an object via the ['key'] notation, you're doing the same thing as accessing it via .key. The compiler is smart enough to know that ['a'] is equivalent to .a. However, when you stick a variable in there, as Bergi mentioned in his comment, the compiler has no idea that [a] is actually ['a'].
It is because local variables(function-scope) become properties of an internal Variable object. So a call to obj[a][b][c][d][e][f] ends up accessing properties a through f on the Variable object first and then on completeObj.
It has nothing to do with variables or variable scope (pure local variables are actually free) but using reflection to access properties rather than constants.
obj['a']['b']['c']['d']['e']['f'];
is equal to obj.a.b.c.d.e.f so it is known even from the source code what properties will be accessed.
However, using bracket notation with variables requires figuring out at runtime what properties will be accessed and so it's completely different. In Java the former code is like using Reflection library to access properties whereas latter is same as using normal static dot access.
So why doesn't the compiler "realize" that the variables are static too? Well your code is completely unreasonable and JIT wasting time optimizing unreasonable code is a bad JIT.
Why does it realize that ['a'] is same as .a though? Well at least in my experience it was much simpler to have parser spit out same MemberAccess objects for dot and bracket access and then just check if the expression is a constant string.
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."