I have an Ajax call where I get back some JavaScript as a String. In the onSuccess Method I want to eval this code. In the JavaScript code there are Function-declarations. All these function should be accessible after the eval.
I made up a small-as-possible example. (The things are going on in the onFailure method in the example, because in JFiddle I can't make a successfull Ajax Call).
You can find the Example here: http://jsfiddle.net/ubXAV/6/
The example you see is working in all browsers (Unfortunately, this will not work in JSFiddle in IE). I marked some lines refering to questions below. Here's the code again:
function evalScript(script)
{
that.eval(script); //1.
}
var that = this;
// AJAX-Call - GadgetActionServlet
new Ajax.Request("THISWILLFAIL.com", {
method: 'post',
onSuccess: function(ajaxResponse) {
alert("success");
},
onFailure: function(){
var script = "{function sayHello(){alert('Hello');}}";
//that.eval(script); //not working in IE 2.
evalScript(script); //working in all browsers
}
});
I read a lot in the internet about scopes and contexts in java but i just can't explain the behaviour here:
Why do I need to call eval on "that" ? According to many sources on the internet the context of a globally defined function is the most global context. (Here it should be window). And the code evaluated through eval should be executed in the context which is calling the eval function.
Assuming, that there is a new global context for the Ajax call (is it?) why can i access the evalScript function but not evaluate the script here directly.
The overall question i have is: Which particular rules apply to the usage of eval? Where are my functions attached to regarding the context? And: does an prototype Ajax call like in the example has its own global object?
First off: If you can avoid using eval, avoid using eval. Does your code have to come back from a POST? Because if you're willing to use GET instead, you can just append a script element to the page:
var script = document.createElement('script');
script.src = "http://example.com" +
"?" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param1value") +
"&" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param2value");
var parent = document.body
|| document.documentElement
|| document.getElementsByTagName('head')[0];
parent.appendChild(script);
Done.
Or if it has to be POST, does it really have to be actual script code? Couldn't it be data that's interpreted by code already on the page? JSON is a useful data format if you can go that way.
But if it has to be POST, and what you get back has to be actual script code as opposed to data, then we'll have to do something like eval. :-)
eval itself is very, very special. It works within the scope in which it's used, even though it looks a bit like a function and that's not how functions work. So actually evaluating script code in global scope is hard unless the eval call is actually at global scope (not within any function call), and of course you can't do that here — you have to trigger this from your ajax callback, and so by definition this happens within a function. (Edit: I just thought of a way to actually use eval at global scope, from within a function. See the update at the end of the answer. But it's evil and horrible and wrong.)
The reason you may have seen advice saying to use window.eval is that a lot of modern browsers offer window.eval (as opposed to eval) which evaluates the given code in global scope. But it's not available on all browsers, and certainly not older ones.
There are workarounds, though. The IE family provides execScript which is very similar to the window.eval offered by other browsers, and in the worst case you can fall back on using a script element. Here's a global eval function that works in nearly everything:
window.evalInGlobalScope = (function() {
var fname, scr;
// Get a unique function name
do {
fname = "__eval_in_global_test_" + Math.floor(Math.random() * 100000);
}
while (typeof window[fname] !== 'undefined');
// Create test script
scr = "function " + fname + "() { }";
// Return the first function that works:
return test(evalInGlobalScope_execScript) ||
test(evalInGlobalScope_windowEval) ||
test(evalInGlobalScope_theHardWay) ||
evalInGlobalScope_fail;
function test(f) {
try {
f(scr);
if (typeof window[fname] === 'function') {
return f;
}
}
catch (e) {
return false;
}
finally {
try { delete window[fname]; } catch (e) { window[fname] = undefined; }
}
}
function evalInGlobalScope_execScript(str) {
window.execScript(str);
}
function evalInGlobalScope_windowEval(str) {
window.eval(str);
}
function evalInGlobalScope_theHardWay(str) {
var parent, script, d = document;
parent = d.body || d.documentElement || d.getElementsByTagName('head')[0];
if (parent) {
script = d.createElement('script');
script.appendChild(d.createTextNode(str));
parent.appendChild(script);
}
}
function evalInGlobalScope_fail() {
throw "evalInGlobalScope: Unable to determine how to do global eval in this environment";
}
})();
..and here's a live example of using it.
Note that all of the code figuring out what to use only runs once; the function that got chosen is assigned to the evalInGlobalScope property on window.
Also note that I haven't given it any return value. That's because the "hard way" version basically can't return any return value, so it's safest if none of them does. Mind you, I'm not sure what browsers still require "the hard way" — nearly everything has execScript and/or window.eval now.
Update: I said above that you couldn't use eval at global scope from within a function. And technically that's true, but I thought of a way to do an end-run around it. It's evil and horrible and wrong, but it does work: Use setTimeout instead, and give it a timeout of 0:
setTimeout("your code here", 0);
When you give setTimeout a string, it performs an eval on it — after the timeout, at global scope.
Again, it's evil and horrible and wrong, and it has the added disadvantage that it's asynchronous (whereas with our evalInGlobalScope function, the eval happens synchronously), but it does...sort of...work. (Live copy) I do not recommend it.
Related
I am trying to create namespaces in JavaScript as in the following script:
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// function script
}
Then I call the function in HTML:
onclick="hlAdmin.editCompany(123)"
I get a reference error: Cannot find "editCompany".
Anyone know why?
Based on your comments I assume the following:
The equivalent script (and scoping is like):
<html><head>
</script>
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// error in this script
}
</script>
</head></body>
<button onclick="hlAdmin.editCompany(123)">Caption</button>
</body></html>
In this example hlAdmin is indeed in the global scope (the root-scope of the host, called window in browsers).
If (in this example) you get reference error: Cannot find "editCompany", then one should look at other error-messages in your (browser's) error-log, because when there is a fatal error in the function for hlAdmin.editCompany, then that function will not be created (hence .editCompany becomes a property that points to undefined instead of a method that points to the function OR .editCompany doesn't even exist (depending on engine/error)).
To investigate if you indeed have a scoping-problem you could test this by: window['hlAdmin'] || (window['hlAdmin']={}); (or some equivalent variant). If that made the code work, then it seems you have some scoping-problem.
Hope these steps help someone in the future.
It's generally considered bad form to mix inline javascript and non-inline. The preferred way to do this would be to keep all the javascript in one place using an event handler:
window.hlAdmin = window.hlAdmin || {};
window.hlAdmin.editCompany = function (src) {
// function script
}
document.getElementById('yourElementId').onclick = function() {
hlAdmin.editCompany(123);
};
To more specifically address the issue: One thing that could cause this issue is if the hlAdmin object is not ending up in the global scope. You stated that this declaration is "at the top of the JavaScript file", but if it's in any kind of function (such as a function set to window.onload, or the jQuery $(function() { ... });) it would not end up in the global scope when declared as a var. A variable declared with var will only end up globally scoped if it's in the root scope, outside of any kind of function. If rather than using var hlAdmin you instead use window.hlAdmin, this will make sure that even if you're inside a document ready function or something similar, you're creating your hlAdmin in the global context, which will fix the problem if it is in fact an issue of scope.
I found the problem.
The browsers (at least Aurora and Chrome) are dropping the namespace in the onclick attribute. When you look at the browser html the namespace has just disappeared from the markup.
Let's start from the code:
function say(name) {
var ghost=function () {
function ghost() {
alert('!');
};
return body;
};
eval("var body=''+"+name+';');
eval(name+('=('+ghost).replace('body', body)+')();');
eval(name+'();');
}
function Baal() {
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
say('Baal'); // or just Baal();
Looks like that saying the devil's name invoke his presence (well, maybe he needs somebody for spiritual possession) ..
As you can see the ghost doesn't exist along with Baal, but we can invoke it since there're evals in say(name).
say(name) reassigns Baal to its code body as a closure and makes it captured a ghost method, that's how things work. But I'm trying to avoid eval ..
So .. let me reword the question:
How do I make a nonexistent(and not a member or global) method invocable without using eval?
Let me rephrase your question, just to make sure I’ve got it. Given a function, you want to put a new variable in its scope, without that scope being the global scope or a scope shared between the caller and the subject, without using eval (or the equivalent new Function and other hacks depending on the environment).
You can’t.
In the case you just mentioned, you could define one function, base(), that uses arguments.callee.caller.
Don’t do that.
The short answer: You don't.
That scope is not available. If you were to attach the scope then it would be available inside of the scope used. You could then access the method handles. I assume this is not what you were looking for, but here is what that would look like. demo
function say(name){
var methods = {};
methods.Baal = function(){
alert("!");
};
return methods[name];//this could invoke as well: methods[name]()
}
var handle = say('Baal');
handle();
What your evals break down to is something along these lines (although with dynamic content from string building - this is the end result)
function say(name) {
var Baal = (function () {
function ghost() {
alert('!');
};
return function(){
if ('undefined'===typeof ghost) {
say('Baal');
return;
}
ghost();
}
})();
Baal();
}
say('Baal'); // or just Baal();
Note that the meat of what happens here is from the function Baal, namely that it calls a hardcoded ghost() which in turn calls a hardcoded alert. Why go through all of this trouble to access a hardcoded function?
A better way would be to inject this function as a callback which expects some parameters to be injected.
jsFiddle Demo
function say(callback){
var params = "!";
if( typeof callback == "function" ){
callback(params);
}
}
say(function(params){
alert(params);
});
It's very difficult for me to read through your code and figure out what you are trying to accomplish with it, but it appears that you are trying to introduce a variable into the current scope so that you can call it. You cannot do this in javascript with the method that you demonstrated. Scoping only ever "flows down". By that I mean that a variable or function defined within a function will only be available to that function and any other functions defined therein. Your function named ghost will only ever be available within the function where it is defined, regardless of when that function is evaluated.
What you can do, however, is write a function that returns a function. You can then call that function and assign the result to a variable in the scope where you want to expose functionality. Doing that would look something like this.
function defineSpecialAlert() {
return function(name) {
alert(name + "!");
};
}
var newlyDefinedMethod = defineSpecialAlert();
newlyDefinedMethod("Baal");
So if I understand, it seems like you want to create an alias of eval: Something like
#Note this code is not intended as a solution, but demonstrates
#an attempt that is guaranteed to fail.
#
function myAlias(ctx) {
eval.call(ctx, 'var ghost = 42');
}
myAlias(this);
alert(ghost);
Javascript allows many funky sleight-of-hand tricks especially with closures, but this is maybe the one impossible thing that javascript cannot do. I've tried at length to do this exact same thing, and I can tell you that you'll run into nothing but complaints from the browser, saying that eval cannot be re-contexted or aliased in any way.
I got some JS Code that gets inside a random Anonymous js function.
I want that code (for example alert('hello') ) to dump/alert
the entire script block/object which it was injected into.
kinda like document.body.innerHTML but for the anonymous function block
result should be like :
Function()({ somecode; MyAlert(...) } )()
or
Try { some code; mycode; } catch(e) { }
Mind your terms. "(browser) script block" literally means script element's code by the spec.
Use "javascript block" or "javascript object" to mean a block or an object.
Do not create confusing new terms; do read and research.
Blocks are not objects; they are language statements.
Just like you cannot "get the code/variables of current line", you cannot "get the code/variables of current block", try block or not.
Stepping back, for now you can use Function.caller to get the function calling your code:
var mycode = function me(){ if ( me.caller ) alert( me.caller.toString() ); };
(function(){ var some = 'code'; mycode(); })();
// Alert "function(){ var some = 'code'; mycode(); }", even when it is anonymous
Note that you get the whole function's code, not the function block's code which excludes parameters and function name.
Function.caller may be removed in future, like arguments.caller. (Both are troubles. What if a cross origin function on the call stack contains private api key code? How should js engines inline your code?)
When the time comes, or when caller is null (when it is global code), you may still be able to get textual stacktrace (new Error().stack) and current script element (document.currentScript), but their capabilities are pretty limited.
You can get a script element's code - if any - with its textContent or innerHTML property.
Your question sounds like an XY Problem. You want to do something that no modern language is meant to do, but never say for what purpose.
Try to describe your real problem.
Functions have a toString() method. (Yes functions have methods!)
var fn = function() { alert('hello') };
fn.toString() // "function() { alert('hello') };"
So you can alert it:
alert(fn.toString());
You can log it to the js console:
console.log(fn.toString());
Or even write it to the page.
document.getElementById('someID').innerHTML = fn.toString();
However, this won't work for every function in the universe.
[].push.toString()
"function push() { [native code] }"
Some functions are not implemented with javascript, but in the compiled code of the browser or JS engine. For these environment provided functions, you will get this above less helpful output.
If you're not in strict mode you can go up the stack from something which was referenceable (i.e. a named function expression) using (non-standard) .caller
function getFunctionReference(callback) {
var ref = getFunctionReference.caller;
if (callback) callback(ref);
return ref;
}
Now you can do things like
(function () {
getFunctionReference(alert);
}());
// alerts the .toString of the IIFE
This only works for functions, you can't do this on top level code.
The best way to explore your code is actually with the Console, and you can use the debugger; statement or breakpoints to follow exactly what is happening and when.
is there a way to tear down a closure in JavaScript to determine what the function is and what the scope is?
Or, maybe more succinctly, is there a way to serialize a closure in JavaScript?
edit
What I am wondering if I am given a function declared as follows:
var o = {};
var f = function() { return o; }
Is there a way to look at just f and find o?
I'm not sure what you mean by "tear down a closure". Determining what the function is can be done using arguments.callee:
function test () { console.log(arguments.callee); }
// -> function test () { console.log(arguments.callee); }
and what the scope is?
The only real way to check to see if a variable is in scope is by trying to access it. That means you need to use a try/catch statement, because an error is thrown if you refer to an undeclared variable:
try{ myVar; alert("myVar is in scope"); }catch (e){ alert("myVar is not in scope"); }
Realistically you would already know which variables are in scope if you wrote the code or even by examining the code if you didn't write it.
If you're trying to get the stack, you can (sort of) do this using the caller property, but it's non-standard and might not be available in all JS implementations*:
function getStack () {
var stack = [], cFunc = arguments.callee;
while (cFunc = cFunc.caller)
stack.push(cFunc);
console.dir(stack);
}
function func1 () { func2(); }
function func2 () { try { obviousError(); } catch (e) { getStack(); } }
Most built-in developer tools give you the stack (IE 7 and IE 8 don't) in a much clearer manner, so it's best to use them where possible.
* Currently all major browsers support Function.caller. It also appears that it is defined in ECMAScript 3.1 - you can check support here.
If I understand you correctly (it's hard to tell, even with your latest edit), the answer is no. One of the purposes of closures is to encapsulate variables, limiting accessibility to groups of related/dependent code (assigned out of the global context), in an effort to minimize name conflicts and/or accidental interaction. If you want to access o in a global context, then you should define it there instead.
[1] Ok, I don't even know how to call this, to be honest. So let me get some semi-pseudo code, to show what I'm trying to do. I'm using jQuery to get an already existing script declared inside the page, inside a createDocument() element, from an AJAX call.
GM_xmlhttprequest({
...
load:function(r){
var doc = document_from_string(r.responseText);
script_content = $('body script:regex(html, local_xw_sig)', doc).html();
var scriptEl = document.createElement('script');
scriptEl.type = 'text/javascript';
scriptEl.innerHTML = script_content; // good till here
(function(sc){
eval(sc.innerHTML); // not exactly like this, but you get the idea, errors
alert('wont get here ' + local_xw_sig); // local_xw_sig is a global "var" inside the source
})(scriptEl);
}
});
So far so good, the script indeed contains the source from the entire script block. Now, inside this "script_content", there are auto executing functions, like $(document).ready(function(){...}) that, everything I "eval" the innerHTML, it executes this code, halting my encapsulated script. Like variables that doesn't exist, etc.
Removing certain parts of the script using regex isn't really an option... what I really wanted is to "walk" inside the function. like do a (completely fictional):
script = eval("function(){" + script_content + "};");
alert(script['local_xw_sig']); // a03ucc34095cw3495
Is there any way to 'disassemble' the function, and be able to reach the "var"s inside of it?
like this function:
function hello(){
var message = "hello";
}
alert(hello.message); // message = var inside the function
Is it possible at all? Or I will have to hack my way using regex? ;P
[2] also, is there any way I can access javascript inside a document created with "createDocument"?
Simply trying to access a local variable inside a function from outside of it is impossible due to scope. However, using closures you can absolutely accomplish this:
function hello(msg){
return function message(){
return msg;
}
}
alert(hello("yourMessage")()); // will alert "yourMessage"
Note exactly what's happening here. You are calling a function which returns a function, in which "yourMessage" is now defined inside its scope. Calling that inner closure the second time will yield that variable you set earlier.
If you are not familiar with closures in JS, I suggest you read this wonderful FAQ.
It's not possible that way. You can introspect object's properties (any function is an object), but not before you have created an instance with new operator.
Looking at your code sample, it seems that your approach is a bit messy – eval()'ing script blocks is something one should not do unless absolutely necessary (a situation I can't imagine).
In your example at
function hello(){
var message = "hello";
}
alert(hello.message); // message = var inside the function
you can in fact use hello.toString() to get the function source, like this:
alert(hello.toString().match(/var message = \"(.*)\";/));
You want to eval the script in global scope. Briefly it is,
// Evalulates a script in a global context
globalEval: function( data ) {
data = jQuery.trim( data );
if ( data ) {
if ( window.execScript )
window.execScript( data );
else if ( jQuery.browser.safari )
// safari doesn't provide a synchronous global eval
window.setTimeout( data, 0 );
else
eval.call( window, data );
}
}
Also check out Google's caja for secure external script evaluation.