Access to locally scoped variable via string – anything but eval? - javascript

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.

Related

Use passed variable name as part of another variable name

blah('A');
function blah(letter){
arrayA.push('something');
}
I want to push something to an array where the name of the array is 'array' plus a letter being passed to it.
I can console out 'arrayA' fine:
console.log('array'+${letter})
But if I try to build the array name, the same logic doesn't work:
array${letter}.push('something')
In the browser (where the global objects, functions, and variables become members of the window object) you can create and access dynamically named objects using the bracket notation.
Were you looking for something like this?
function blah(letter){
window['array' + letter] = [];
window['array' + letter].push('something');
}
blah('A');
After this you can access and use the newly created array (arrayA) as usual.
arrayA.push('something else');
In node you can probably achieve this using global instead of window.
Try it and forget it (or replace the 3 occurrences of window with global for testing with node.js):
function test(name,value){
if(!window["array"+name])
window["array"+name]=[];
window["array"+name].push(value);
}
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
test("A",10);
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
test("A",20);
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
window is the global scope in a browser, and generally you should not rely on global variables without a good reason. They lack context (that is why they are 'global'), making it hard to tell where they belong, what they are and where they come from. That is something what most programming paradigms advise against.
The thing also works with node.js, just it has global as global context, you can paste this snippet into https://www.tutorialspoint.com/execute_nodejs_online.php as a test, replace the 3 window-s, and it will work (you can of course wrap it into a proper module too, just that is more work). What is written above against the usage of global variables stays true for node.js too. Do not use the global context especially if you are developing modules.
However, instead of window, the syntax works with any object too, and that would be considered okay:
var obj={};
console.log(obj.something);
obj['some'+'thing']=10;
console.log(obj.something);
So you can freely have your own 'context' object (if you write the var obj={}; line in the top-level of a module, it will be available everywhere in that module, and it will not interfere with the outside world), and create/access its members using this array-like syntax (obj['something']), constructing the names on the fly when necessary.

Searching local javascript variables by name patterns

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.

Destructuring an object without specifying its properties

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.

temporary global variables/functions and prototype overrides to write a JS DSL

I'm exploring a variety of options for a JavaScript routing framework that I'm working on, and I'd like to provide a DSL written in JavaScript for defining the router.
I had the idea of using temporary prototype overrides on the String class (maintain a hash of the previous prototype values, override, run the DSL code, reset the prototype values to what they were) to all for something like this:
DSL.run(function() {
"hello".isSomething();
"foo".isSomethingElse();
});
The other idea was to use define temporary global variables and then remove/reset them after the DSL is done running. That way, if you run the DSL closure with window (or whatever the global object is) as the this context, I believe you should be able to do something like:
DSL.run(function() {
defineSomething("hello");
defineSomethingElse("foo");
});
I know I know I know I should be super careful about the prototype overloads and polluting the global namespace, but this seems to be a pretty localized and easily cleanup-able approach to keep that sort of thing from happening. My question is, are there any other considerations that would keep this from being a reality?
One potential problem I could think of is whether this would work in a Node.js setting, where code is stored in separate modules and global variables kept from each other, which I think would eliminate option B, but what about String prototype overloads? Those are shared between modules, right? e.g. if I include module A, which sets String prototype values, those prototype values will be available in the including code, right?
Also, let me know if anyone's done this sort of thing before. I think it's a clever approach to this sort of problem and I haven't seen anything quite like it, but I want to make sure I'm not leaving out something really obvious and damning.
Use delete String.prototype[method].
var dsl = function(f){
var _ = String.prototype;
_.isSomething = function(){
console.log('isSomething: '+this);
}
_.isSomethingElse = function(){
console.log('isSomethingElse: '+this);
}
f();
delete _.isSomething;
delete _.isSomethingElse;
}
dsl(function(){
"hello".isSomething(); // isSomething: hello
"foo".isSomethingElse(); // isSomethingElse: foo
});
// "hello".isSomething(); // error "Object has no method 'isSomething'"
// "foo".isSomethingElse(); // error "Object has no method 'isSomethingElse'"

SpiderMonkey: How do i get the name of the current JSObject from c?

someone asked how to get the value of a JSObject property from c. That helped me a bit.
But, does anyone know how to get the current JavaScript name of an object from c?
example:
var foo={prop:'bar'};
then somewhere for example in jsapi.cpp:
JS_somemethod(JSContext *cx, JSObject *obj){
//how do i get the name 'foo' (or the current alias) here if *obj points to the foo (or whatever alias name) object?
}
Thx for hints and answers!
Okay, just to document the question clarification from your comment I'll repeat it here:
Maybe i tell you in short my purpose: For the integration of new security system in webbrowsers i need to find out what is accessed during a common session on a website. my aim is to get something like a log which objects are accessed and how (read,write,execute). for example: window.alert (x) window.foo.bar (w) ... you now why i need the names of the variables? maybe you've got an other idea?
Just to say it up front, this is pretty tricky in general. There are a few options available to you, all of which are somewhat difficult:
Use the debugging API, either via the cool new debugger object API or via jsdbgapi.cpp (the complex and slightly gross interface that firebug uses). You can use debugger functionality to trap on all property accesses, functions calls, and local references in order to dump out interesting properties of objects.
If you're only interested in the way that your (user defined) objects are accessed, you can use Proxies that wrap your objects and dump out the accesses being performed on them. A slightly more powerful and low-level version of proxies are actually used in the browser to implement our security features. (Additionally, most of the proxy functionality is accessible for custom-created objects in the JSAPI via our meta-object-protocol, but proxies are just a much cleaner version of that same functionality.)
Instrument the interpreter loop in jsinterp.cpp. A lot of research-like work turns off the JITs and instruments the interpreter loop, because it's fairly understandable if you've worked on language implementations before. Unfortunately, it's not always easy to translate what you want to do into interpreter loop instrumentation at first, even if you have experience with language implementations.
The debugger object is definitely my favorite option of those I've listed here. If you go with that approach and end up getting stuck feel free to visit #jsapi in irc.mozilla.org and try to get a hold of jorendorff or jimb, they're a) awesome and b) the creators of the debugger object.
Without looking too closely at the API, I'm going to say that while it might be possible to do this in some very basic cases, you don't want to. This is why:
In Javascript, as in most languages, variables point to values. Variables are not the values themselves. The object is not in any way inherently related to the name foo.
For instance, you can do
var foo = {prop: 'bar'};
var fooo = foo;
var foooo = foo;
var quux = {q: foo, r: foo, s: [foo]};
All of those foos are now the exact same foo; which one do you want back? There's no API call for that because it's too ambiguous and not terribly useful.
However, if you really want to find one of the global variables holding a value, you can try iterating over the keys on the global object and testing them for the value.
for(var key in this){
if(this[key] == myval){
var myname = key;
}
}
You'd have to translate this into your API calls or else put it in a function and call that through the API.
The more simple and straightforward solution would be to figure out what you want to do with foo later on, and pass in a callback function that will do that, e.g. with JS_CallFunction.
tl;dr: Previous paragraph.

Categories

Resources