Evaluating computed code that accesses locally scoped variables - javascript

I'm trying to create a function that returns a callback, which when called, will access the callers local scope parameters.
Consider the following code:
function caller() {
let param = "original scope param.";
let wrapped = callee();
eval(wrapped.toString())(); // Works
wrapped(); // Doesn't work
}
function callee() {
return () => eval("console.log('Im accessing ' + param)");
}
caller()
calling eval in the caller function works, but I don't control that function, so I can't make it eval my output.
If I could access the Scope Chain, my problem would be solved, but I didn't find anything that does that. Any help would be appreciated, even if the solution is very dirty.

Related

Where does a returned function go if not stored somewhere?

Where does a returned function go if not stored somewhere? Shouldn't it get appended to the global object/current outer-context?
Here's an example:
var setup = function(){
console.log("xyz");
return function goBack(){
console.log("It's actually abc");
}
}
Now, on calling setup() in the global scope, "xyz" is being shown in the console, but the returning function, i.e goBack is not being appended in the global scope.
setup() //Outputs "xyz"
Now, on trying to call goBack, it's undefined in global scope:
goBack() //error: goBack not defined
Now I could access goBack using setUp()() or by storing the returned function from setup() into a global variable. But, shouldn't I be able to access goBack from the global scope once I execute setup() ? Because if I had stored setup() into a global variable, I would have access to goBack via that variable. But what happens if I don't use a variable to store the returned function from setup()? Where does goBack return to exactly? Thank you.
You're returning a function object, which is the same as any other kind of object, or any other sort of value. If you don't assign it to anything, it simply goes out of scope and will be garbage collected. Nothing happens with it.
I would say that you could not execute the goBackfunction because of the lexical scope : https://stackoverflow.com/a/1047491/1836175 not closures : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures.
If you want to execute your goBackfunction because it is returned by setup() you simply need to store the return of the setup function. Javascript has First-class functions : https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function
So the code to be able to execute the goBack function later would be :
var later = setup()
// do whatever you want and when needed
later() // -> will call the goBack function
Hope that helps to clarify what it is goind on :).
PS : and so yes, if not stored the function will be garbage collected (see deceze response).
If you don't store it in a variable it will be garbage collected when the setup function returns and you can't access it any more. On the other hand you can write your setup function like this:
var setup = function(){
console.log("xyz");
window.goBack = function(){
console.log("It's actually abc");
}
}
and have the goBack function available on the global scope.
then you would access it via window.goBack() or just goBack()
Actually, you are returning an object, which can be stored within a variable and then ran, or, you can also run it directly.
function a(){
console.log("a");
return function b(){
console.log("b");
};
}
console.log("this is calling just a");
a();
console.log("this is calling the a and the returned object");
a()();
Just to add to deceze's answer - adding to global scope is generally a Bad Thing, so the language doesn't do it unless you really want it to.
An example - suppose you have a large application with lots of component bits, and many of those component bits return a function when called. Now you have to make sure every single component uses a different name for their function, otherwise you could end up overwriting someone else's global, which would be bad. And if you're using a javascript library, you need to make sure that you're not using any of the same names as the library, because then you could break their code.
Keeping stuff in scope is super important, and storing stuff in global is a good way to introduce bugs that are really really hard to fix.

Differences when using functions for casper.evaluate

I'm using PhantomJS v2.0 and CasperJS 1.1.0-beta3. I want to query a specific part inside the page DOM.
Here the code that did not work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc());
this.echo("value: " + del);
And here the code that did work:
var del=this.evaluate(function()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
});
this.echo("value: " + del);
It seems to be the same, but it works different, I don't understand.
And here a code that did also work:
function myfunc()
{
return document.querySelector('span[style="color:#50aa50;"]').innerText;
}
var del=this.evaluate(myfunc);
this.echo("value: " + del);
The difference here, I call the myfunc without the '()'.
Can anyone explain the reason?
The problem is this:
var text = this.evaluate(myfunc());
Functions in JavaScript are first class citizen. You can pass them into other functions. But that's not what you are doing here. You call the function and pass the result into evaluate, but the result is not a function.
Also casper.evaluate() is the page context, and only the page context has access to the document. When you call the function (with ()) essentially before executing casper.evaluate(), you erroneously try to access the document, when it is not possible.
The difference to casper.evaluate(function(){...}); is that the anonymous function is defined and passed into the evaluate() function.
There are cases where a function should be called instead of passed. For example when currying is done, but this is not applicable to casper.evaluate(), because it is sandboxed and the function that is finally run in casper.evaluate() cannot use variables from outside. It must be self contained. So the following code will also not work:
function myFunc2(a){
return function(){
// a is from outer scope so it will be inaccessible in `evaluate`
return a;
};
}
casper.echo(casper.evaluate(myFunc2("asd"))); // null
You should use
var text = this.evaluate(myfunc);
to pass a previously defined function to run in the page context.
It's also not a good idea to use reserved keywords like del as variable names.

How to access javascript function in the eval() scope

I use eval() to execute javascript that is returned by an ajax request from the server. However, I cant call a function that was created with eval() and got a ReferenceError: function is not defined.
Is it something normal that functions inside a javascript that was executed with eval() cannot be accessed? Is there a way to access such functions?
I think this simple jsFiddle illustrate the problem: http://jsfiddle.net/M2GLs/
The created function isn't in the correct scope. So your onclick can't 'see' it. Use
window.addFeatureToTable = function() {
// definition
}
to force it in the window-scope.
Working JsFiddle
To answer your question in the comment:
What you actualy have is something like this code:
function a()
{
function b(where) {
alert('b can be called inside a, but not outside, we are now ' + where);
}
b('inside');
}
a();
b('outside');
b is defined in the scope of a, and can only be accessed within this scope (demo). In your case the function-definition is within an eval, but the same rule aplies there. In that case within the scope of function(r). You can't access the scope of this function from within the a.onclick, so you have to change the function-definition. Alternatively you can bind the on-click just after the eval (jsFiddle), since it is then still in scope:
js = "function someFunction() { alert('function called') }"
eval(js)
document.getElementById('myA').onclick = someFunction;

How to execute different partsof the JS code in one scope

I have several script blocks depend on each other. I need to perform them in one scope.
My attempt:
var scopeWrapper = {};
with(scopeWrapper) {
(function() {
this.run = function(code) {
eval(code);
};
}).call(scopeWrapper);
}
scopeWrapper.run('function test() { alert("passed"); }');
scopeWrapper.run('test();');
I get 'test is not defined' error. It seems that the code is executed in different scopes.
Why is this happening?
Edit: Bergi pointed out my original answer was wrong, he is correct. Since eval runs in its own scope and the function constructor still runs in function scope according to the spec this is not possible with either.
While I have done this sort of thing myself several times with node.js using the vm module where you get much finer grain of control over where your code executes, it seems browsers require a different approach.
The only way you can share variables in such a way is to do so in the global scope of JavaScript execution (possibly, in an iframe). One way you could do this is script tag injection.
function run(code){
var sc = document.createElement("script");
sc.setAttribute("type","text/javascript");
sc.innerHTML = code;
document.body.appendChild(sc);
}
run("var x = 5");
run("document.write(x)");
(here is this code in action)
As for the scope wrapper, instead of injecting them in the same frame inject them in another iframe. That will scope their window object to that iframe and will allow you to share context.
I humbly apologize for my previous answer, I misread the spec. I hope this answer helps you.
I'm leaving my previous answer here because I still believe it provides some insight into how eval and the Function constructor work.
When running code in non-strict mode eval runs in the current context of your page
After your function declaration is done, the scope it was declared in dies, and with it the function.
Consider using the Function constructor and then .calling it
In your case that would be something like:
var scopeWrapper = {};
scopeWrapper.run = function(code){
var functionToRun = new Function(code);
functionToRun.call(scopeWrapper);
}
scopeWrapper.run('this.test = function() { alert("passed"); }');
scopeWrapper.run("this.test()")
Here is a reference directly from the spec:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
Initialize the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
If this code is run in the node.js consider using the vm module. Also note that this approach is still not secure in the way it'll allow code you run to change your code.
test only exists in the scope of this.run and only at call time :
// global scope
(function(){
// local scope (equivalent of your "run" function scope)
eval('function f(){};');
console.log(f); // prints "function f(){}"
})();
console.log(f); // prints "ReferenceError: f is not defined"
Each call of run creates a new scope in which each code is evaluated separately.

Expecting the right calling context (this) in the JavaScript object

Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error

Categories