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.
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 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.
In python _ is used as a throwaway variable. Is there an idiomatic accepted name in javascript?
Rigth now, you can use array destructuring, no need for a var.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
For example:
[,b] = [1,2];
console.log(b);
will output :
2
And the value 1 won't be assigned to any unused var.
There isn't an accepted throwaway variable naming in JavaScript like in Python. Although, generally in the software engineering world, engineers tend to call those variables:
dummy, temp, trash, black_hole, garbage.
Use these variable namings or whatever name that will imply to the engineer reading your code that these variables have no specific use.
With that in mind, I would strongly not recommend using signs such as below as a throwaway variable:
_, $
Unless there is an explicit convention like in Python, don't use these signs as a throwaway variable, otherwise, it may interfere with other naming conventions and libraries in other languages, like the underscore and the jQuery libraries in JS.
And of course, if you find a specific use for that variable later on, make sure to rename it.
Generally, you will declare the signature of a function as an array. Simply omit one of them
const foo = (bar,baz,) => bar+baz;
foo(3,4,5) // → 7
This obviously works as long as the declaration of function argument is the last one; unlike this answer, which is valid for already-declared variables embedded in arrays. This:
const foo = (, bar,baz) => bar+baz;
will fail with:
Uncaught SyntaxError: expected expression, got ','
You can hack your way into this using any expression. However, you'll need to use the self same expression in every invocation
const quux = ([], bar,baz) => bar+baz;
quux(42,3,4); // → Uncaught TypeError: (destructured parameter) is not iterable
quux([],3,4); // → 7
That constant can be anything, so you can as well call it "dummy", but you will still have to repeat in every invocation.
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'"