How to get variables names inside a javascript function - javascript

I want to get "a, b" in this example https://jsfiddle.net/8dw9e5k7/ instead I get something else. Is it possible ?
function test() {
a = 5;
b = 10;
try {
var variables = ""
for (var name in this)
variables += name + "\n";
alert(variables);
} catch(e) {
alert(e.message)
}
}
test();

Assigning non-existent variables in a function makes the variables global which means attached to the window object. That's why your code works somewhat and displays the variables along with many other things, because this refers to window.
Dumping your own variables attached to window is not possible in a clean way (some hacks still exist which consist in creating a fresh window with an iframe and compare it to the current window, check this answer if you're interested)
Now if you're expecting this to refer to the lexical scope of the current function, it's not the case and you can't retrieve it programmatically at all.

Related

JavaScript Function Related Issue [duplicate]

Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>

Assigning value to declared variable vs. assigning variable to non-declared variable? [duplicate]

Is there any difference between declaring a variable:
var a=0; //1
...this way:
a=0; //2
...or:
window.a=0; //3
in global scope?
Yes, there are a couple of differences, though in practical terms they're not usually big ones.
There's a fourth way, and as of ES2015 (ES6) there's two more. I've added the fourth way at the end, but inserted the ES2015 ways after #1 (you'll see why), so we have:
var a = 0; // 1
let a = 0; // 1.1 (new with ES2015)
const a = 0; // 1.2 (new with ES2015)
a = 0; // 2
window.a = 0; // 3
this.a = 0; // 4
Those statements explained
#1 var a = 0;
This creates a global variable which is also a property of the global object, which we access as window on browsers (or via this a global scope, in non-strict code). Unlike some other properties, the property cannot be removed via delete.
In specification terms, it creates an identifier binding on the object Environment Record for the global environment. That makes it a property of the global object because the global object is where identifier bindings for the global environment's object Environment Record are held. This is why the property is non-deletable: It's not just a simple property, it's an identifier binding.
The binding (variable) is defined before the first line of code runs (see "When var happens" below).
Note that on IE8 and earlier, the property created on window is not enumerable (doesn't show up in for..in statements). In IE9, Chrome, Firefox, and Opera, it's enumerable.
#1.1 let a = 0;
This creates a global variable which is not a property of the global object. This is a new thing as of ES2015.
In specification terms, it creates an identifier binding on the declarative Environment Record for the global environment rather than the object Environment Record. The global environment is unique in having a split Environment Record, one for all the old stuff that goes on the global object (the object Environment Record) and another for all the new stuff (let, const, and the functions created by class) that don't go on the global object.
The binding is created before any step-by-step code in its enclosing block is executed (in this case, before any global code runs), but it's not accessible in any way until the step-by-step execution reaches the let statement. Once execution reaches the let statement, the variable is accessible. (See "When let and const happen" below.)
#1.2 const a = 0;
Creates a global constant, which is not a property of the global object.
const is exactly like let except that you must provide an initializer (the = value part), and you cannot change the value of the constant once it's created. Under the covers, it's exactly like let but with a flag on the identifier binding saying its value cannot be changed. Using const does three things for you:
Makes it a parse-time error if you try to assign to the constant.
Documents its unchanging nature for other programmers.
Lets the JavaScript engine optimize on the basis that it won't change.
#2 a = 0;
This creates a property on the global object implicitly. As it's a normal property, you can delete it. I'd recommend not doing this, it can be unclear to anyone reading your code later. If you use ES5's strict mode, doing this (assigning to a non-existent variable) is an error. It's one of several reasons to use strict mode.
And interestingly, again on IE8 and earlier, the property created not enumerable (doesn't show up in for..in statements). That's odd, particularly given #3 below.
#3 window.a = 0;
This creates a property on the global object explicitly, using the window global that refers to the global object (on browsers; some non-browser environments have an equivalent global variable, such as global on NodeJS). As it's a normal property, you can delete it.
This property is enumerable, on IE8 and earlier, and on every other browser I've tried.
#4 this.a = 0;
Exactly like #3, except we're referencing the global object through this instead of the global window. This won't work in strict mode, though, because in strict mode global code, this doesn't have a reference to the global object (it has the value undefined instead).
Deleting properties
What do I mean by "deleting" or "removing" a? Exactly that: Removing the property (entirely) via the delete keyword:
window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"
delete completely removes a property from an object. You can't do that with properties added to window indirectly via var, the delete is either silently ignored or throws an exception (depending on the JavaScript implementation and whether you're in strict mode).
Warning: IE8 again (and presumably earlier, and IE9-IE11 in the broken "compatibility" mode): It won't let you delete properties of the window object, even when you should be allowed to. Worse, it throws an exception when you try (try this experiment in IE8 and in other browsers). So when deleting from the window object, you have to be defensive:
try {
delete window.prop;
}
catch (e) {
window.prop = undefined;
}
That tries to delete the property, and if an exception is thrown it does the next best thing and sets the property to undefined.
This only applies to the window object, and only (as far as I know) to IE8 and earlier (or IE9-IE11 in the broken "compatibility" mode). Other browsers are fine with deleting window properties, subject to the rules above.
When var happens
The variables defined via the var statement are created before any step-by-step code in the execution context is run, and so the property exists well before the var statement.
This can be confusing, so let's take a look:
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar); // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar); // displays "b"
Live example:
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar); // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar); // displays "b"
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
As you can see, the symbol foo is defined before the first line, but the symbol bar isn't. Where the var foo = "f"; statement is, there are really two things: defining the symbol, which happens before the first line of code is run; and doing an assignment to that symbol, which happens where the line is in the step-by-step flow. This is known as "var hoisting" because the var foo part is moved ("hoisted") to the top of the scope, but the foo = "f" part is left in its original location. (See Poor misunderstood var on my anemic little blog.)
When let and const happen
let and const are different from var in a couple of ways. The way that's relevant to the question is that although the binding they define is created before any step-by-step code runs, it's not accessible until the let or const statement is reached.
So while this runs:
display(a); // undefined
var a = 0;
display(a); // 0
This throws an error:
display(a); // ReferenceError: a is not defined
let a = 0;
display(a);
The other two ways that let and const differ from var, which aren't really relevant to the question, are:
var always applies to the entire execution context (throughout global code, or throughout function code in the function where it appears), but let and const apply only within the block where they appear. That is, var has function (or global) scope, but let and const have block scope.
Repeating var a in the same context is harmless, but if you have let a (or const a), having another let a or a const a or a var a is a syntax error.
Here's an example demonstrating that let and const take effect immediately in their block before any code within that block runs, but aren't accessible until the let or const statement:
var a = 0;
console.log(a);
if (true)
{
console.log(a); // ReferenceError: a is not defined
let a = 1;
console.log(a);
}
Note that the second console.log fails, instead of accessing the a from outside the block.
Off-topic: Avoid cluttering the global object (window)
The window object gets very, very cluttered with properties. Whenever possible, strongly recommend not adding to the mess. Instead, wrap up your symbols in a little package and export at most one symbol to the window object. (I frequently don't export any symbols to the window object.) You can use a function to contain all of your code in order to contain your symbols, and that function can be anonymous if you like:
(function() {
var a = 0; // `a` is NOT a property of `window` now
function foo() {
alert(a); // Alerts "0", because `foo` can access `a`
}
})();
In that example, we define a function and have it executed right away (the () at the end).
A function used in this way is frequently called a scoping function. Functions defined within the scoping function can access variables defined in the scoping function because they're closures over that data (see: Closures are not complicated on my anemic little blog).
Keeping it simple :
a = 0
The code above gives a global scope variable
var a = 0;
This code will give a variable to be used in the current scope, and under it
window.a = 0;
This generally is same as the global variable.
<title>Index.html</title>
<script>
var varDeclaration = true;
noVarDeclaration = true;
window.hungOnWindow = true;
document.hungOnDocument = true;
</script>
<script src="external.js"></script>
/* external.js */
console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8
console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8
console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8
console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!) *I personally find this more clugy than hanging off window obj
Is there a global object that all vars are hung off of by default? eg: 'globals.noVar declaration'
Bassed on the excellent answer of T.J. Crowder: (Off-topic: Avoid cluttering window)
This is an example of his idea:
Html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="init.js"></script>
<script type="text/javascript">
MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
</script>
<script src="script.js"></script>
</head>
<body>
<h1>Hello !</h1>
</body>
</html>
init.js (Based on this answer)
var MYLIBRARY = MYLIBRARY || (function(){
var _args = {}; // private
return {
init : function(Args) {
_args = Args;
// some other initialising
},
helloWorld : function(i) {
return _args[i];
}
};
}());
script.js
// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);
alert(a);
Here's the plnkr. Hope it help !
In global scope there is no semantic difference.
But you really should avoid a=0 since your setting a value to an undeclared variable.
Also use closures to avoid editing global scope at all
(function() {
// do stuff locally
// Hoist something to global scope
window.someGlobal = someLocal
}());
Always use closures and always hoist to global scope when its absolutely neccesary. You should be using asynchronous event handling for most of your communication anyway.
As #AvianMoncellor mentioned there is an IE bug with var a = foo only declaring a global for file scope. This is an issue with IE's notorious broken interpreter. This bug does sound familiar so it's probably true.
So stick to window.globalName = someLocalpointer

Reading a variable value inside a named function in javascript

This may be very simple but i lost my way in finding the answer,
I have a named function inside a Backbone view. I'm trying to write unit test for this named function and i could see a variable is being declared in function scope. The state of this variable changes based on certain scenarios.
I have instantiated the view object inside my test suite and when i do a console.log(viewObject.namedFunction) i see the entire function being logged properly. Now how do i access the variable inside the namedFunction, i was expecting something like viewObject.namedFunction.variableName but it will not work and when i did that, it was not working as expected.
If the variable is tied to viewObject scope then it would have been easy to access it. But in this scenario it is not so can some one please help me in getting a hold on the variable inside named function in view object from test suite.
I think I understand your confusion, because when you define a variable using var in the window scope, it becomes a property on the window object... It would seem to follow that when you define a variable in a child scope, it should become a member of the local context, right? Nope. Globals are special ;-)
In fact, if that were the case, there would be no privacy!
If you want sign to be public, be explicit.
obj = {
namedFunction : function myself() {
console.log(myself);
myself.sign = 23;
}
};
obj.namedFunction.sign;
The only problem with this approach is that you can now assign a new value to sign from anywhere. The classic and preferred approach to this problem is to create a getter function for your private variable.
var obj = {
namedFunction : function myself() {
var sign = 3;
myself.getSign = function(){return sign;};
}
};
obj.namedFunction.getSign();
This isn't by any means the only approach, but I hope it was helpful.
One way to do this is like such:
namedFunction = (function(){
var theActualFunction = function(){
//Do something
//Access variable like: theActualFunction.MyVariable
};
theActualFunction.MyVariable = "value";
return theActualFunction;
}());

How can I encapsulate object property so that previous object isn't taken over? (Prototypes/closure.)

I'm starting to get my head around prototyping and closures, within Javascript, but not quite. This example below, my two objects, the second object seems to lose scope/context and takes over the first objects identity.
function broker()
{
var _q = [];
this.add = function(delegate) {_q[_q.length] = delegate; }
this.broadcast = function(message)
{
for(qi = 0; qi < _q.length; qi++)
{
_q[qi](message);
}
}
}
function subscriber(abroker, yourname)
{
me = this;
this.myprop = yourname;
this.subby = function(message){ alert(message + " " + me.myprop + me.dosomething() + secret()); };
this.dosomething = function() {return "...abc";};
function secret(){return "...def";}
abroker.add(this.subby);
}
var thebroker = new broker();
var mysub = new subscriber(thebroker, 'mysub');
var myothersub = new subscriber(thebroker, 'myothersub');
thebroker.broadcast("hello from");
The idea is that there is a common broker object that can invoke a delegate on subscriber objects and execute functions within. But I'm losing scope within the invoked function called by the broker.
output: 2 alerts windows, both output: "myothersub", mysub seems to lose scope?
I have successfully achieved the correct response by explicitly declaring the subby delegate outside of the original object, and referencing the entire object, e.g:
Instead of declaring this.subby within the subscriber object:
mysub.subby = function(message)
{
alert(message + " " + mysub.myprop); // obv the dosomething his hidden
}
thebroker.add(mysub.subby);
Excuse me if any of the above syntax is wrong, from typing directly from memory. It does work in practice, but loses the encapsulation I'm used to.
How can I encapsulate using the original technique within out losing scope/context of the object?
SHORT ANSWER: It looks as though the problem is simply to do with your declaration of me in the subscriber constructor. At the minimum you need to put a var in front of it to make it a local/private variable for each object. So var me = this; instead of me = this;.
EXPLANATION: In JavaScript, when you don't explicitly declare a variable with var, it makes the variable global. So what was happening in your original script is that you created mysub which declared me as a global reference to the this inside the mysub object. But as soon as you created myothersub the global me was overwritten to the this inside the new myothersub object.
Because your subby method created an alert that was based on me it didn't matter which object you called it from since the method in both objects was not using anything local or specific to the object but merely referencing the same global variable -- the this inside the last such object to be created
By simply writing var me = this; instead of me = this; you create a local version of me within a closure each time for each new object you create and not one that is overwritten.
...
PS. Extra tip. You should do this with all variables to ensure that you have as few globals as possible, especially when you don't mean them to be global! Therefore I'd make the same declaration for the variable qi inside the broker constructor. You could do this simply by declaring inside the loop conditions, e.g., for (var qi = 0; qi < _q.length; qi++). That would be enough to stop qi being a global variable.
However, in the interests of keeping your code easy to read it's best to declare all variables at the top of a function. So I would recommend simply rewriting the broadcast method thus:
this.broadcast = function(message) {
var qi, // STOPS `qi` BECOMING A GLOBAL
ql = q.length; // SO DON'T HAVE TO CHECK LENGTH OF `q` EVERY LOOP
for(qi = 0; qi < ql; qi += 1) {
_q[qi](message);
}
};
If you haven't come across him before, Douglas Crockford is a really good go-to writer on JavaScript when it comes to closures, object creation and just good code writing conventions. There's a bunch of tips on this page and you can find videos of him lecturing on the topic quite easily. All this really helped me when I started looking into closures and other aspects of JavaScript more closely; hope it helps you too.

Restricting eval() to a narrow scope

I have a javascript file that reads another file which may contain javascript fragments that need to be eval()-ed. The script fragments are supposed to conform to a strict subset of javascript that limits what they can do and which variables they can change, but I want to know if there is some way to enforce this by preventing the eval from seeing variables in the global scope. Something like the following:
function safeEval( fragment )
{
var localVariable = g_Variable;
{
// do magic scoping here so that the eval fragment can see localVariable
// but not g_Variable or anything else outside function scope
eval( fragment );
}
}
The actual code doesn't need to look like this--I'm open to any and all weird tricks with closures, etc. But I do want to know if this is even possible.
Short answer: No. If it's in the global scope, it's available to anything.
Long answer: if you're eval()ing untrusted code that really wants to read or mess with your execution environment, you're screwed. But if you own and trust all code being executed, including that being eval()ed, you can fake it by overriding the execution context:
function maskedEval(scr)
{
// set up an object to serve as the context for the code
// being evaluated.
var mask = {};
// mask global properties
for (p in this)
mask[p] = undefined;
// execute script in private context
(new Function( "with(this) { " + scr + "}")).call(mask);
}
Again, I must stress:
This will only serve to shield trusted code from the context in which it is executed. If you don't trust the code, DO NOT eval() it (or pass it to new Function(), or use it in any other way that behaves like eval()).
Shog9♦'s Answer is great. But if your code is just an expression, the code will be executed and nothing will be returned. For expressions, use
function evalInContext(context, js) {
return eval('with(context) { ' + js + ' }');
}
Here is how to use it:
var obj = {key: true};
evalInContext(obj, 'key ? "YES" : "NO"');
It will return "YES".
If you are not sure if the code to be executed is expressions or statements, you can combine them:
function evalInContext(context, js) {
var value;
try {
// for expressions
value = eval('with(context) { ' + js + ' }');
} catch (e) {
if (e instanceof SyntaxError) {
try {
// for statements
value = (new Function('with(this) { ' + js + ' }')).call(context);
} catch (e) {}
}
}
return value;
}
Similar to the dynamic function wrapping script in a with block approach above, this allows you to add pseudo-globals to the code you want to execute. You can "hide" specific things by adding them to the context.
function evalInContext(source, context) {
source = '(function(' + Object.keys(context).join(', ') + ') {' + source + '})';
var compiled = eval(source);
return compiled.apply(context, values());
// you likely don't need this - use underscore, jQuery, etc
function values() {
var result = [];
for (var property in context)
if (context.hasOwnProperty(property))
result.push(context[property]);
return result;
}
}
See http://jsfiddle.net/PRh8t/ for an example. Note that Object.keys is not supported in all browsers.
You cant limit the scope of eval
btw see this post
There may be some other way to accomplish what it is you want accomplish in the grand scheme of things but you cannot limit the scope of eval in any way. You may be able to hide certain variables as pseudo private variables in javascript, but I dont think this is what you're going for.
There is a project called Google Caja. You can "sandbox" third party javascript using Caja. https://developers.google.com/caja/
Here's an idea. What if you used a static analyzer (something you could build with esprima, for example) to determine which outside variables the eval'd code uses, and alias them. By "outside code" i mean variables the eval'd code uses but does not declare. Here's an example:
eval(safeEval(
"var x = window.theX;"
+"y = Math.random();"
+"eval('window.z = 500;');"))
where safeEval returns the javascript string modified with a context that blocks access to outside variables:
";(function(y, Math, window) {"
+"var x = window.theX;"
+"y = Math.random();"
+"eval(safeEval('window.z = 500;');"
"})();"
There are a couple things you can do now with this:
You can ensure that eval'd code can't read the values of outside variables, nor write to them (by passing undefined as the function arguments, or not passing arguments). Or you could simply throw an exception in cases where variables are being unsafely accessed.
You also ensure that variables created by eval don't affect the surrounding scope
You could allow eval to create variables in the surrounding scope by declaring those variables outside the closure instead of as function parameters
You could allow read-only access by copying values of outside variables and using them as arguments to the function
You could allow read-write access to specific variables by telling safeEval to not alias those particular names
You can detect cases where the eval does not modify a particular variable and allow it to be automatically excluded from being aliased (eg. Math in this case, is not being modified)
You could give the eval a context in which to run, by passing in argument values that may be different than the surrounding context
You could capture context changes by also returning the function arguments from the function so you can examine them outside the eval.
Note that the use of eval is a special case, since by its nature, it effectively can't be wrapped in another function (which is why we have to do eval(safeEval(...))).
Of course, doing all this work may slow down your code, but there are certainly places where the hit won't matter. Hope this helps someone. And if anyone creates a proof of concept, I'd love to see a link to it here ; )
Don't execute code you don't trust. Globals will always be accessible.
If you do trust the code, you can execute it with particular variables in it's scope as follows:
(new Function("a", "b", "alert(a + b);"))(1, 2);
this is equivalent to:
(function (a, b) {
alert(a + b);
})(1, 2);
I accidentally found out I can use Proxy to restrict the scope object, it seems be a lot easier to mask the variable out of the scope. I'm not sure if this method have disadvantages, but so far its work well for me.
function maskedEval(src, ctx = {})
{
ctx = new Proxy(ctx, {
has: () => true
})
// execute script in private context
let func = (new Function("with(this) { " + src + "}"));
func.call(ctx);
}
a = 1;
maskedEval("console.log(a)", { console });
maskedEval("console.log(a)", { console, a: 22});
maskedEval("a = 1", { a: 22 })
console.log(a)
Don't use eval. There's an alternative, js.js: JS interpreter written in JS, so that you can run JS programs in any environment you've managed to setup. Here's an example of its API from the project page:
var jsObjs = JSJS.Init();
var rval = JSJS.EvaluateScript(jsObjs.cx, jsObjs.glob, "1 + 1");
var d = JSJS.ValueToNumber(jsObjs.cx, rval);
window.alert(d); // 2
JSJS.End(jsObjs);
Nothing scary, as you can see.

Categories