For our internal js framework, to ensure good usage of the framework, I want to forbid the usage of Ajax requests in some parts of the framework.
Is there a way to achieve something similar to this:
function doSomething() {
instructions with ajax calls...
withAjaxForbidden(function() {
instructions using ajax calls should raise exception here
});
instructions with ajax calls...
}
Is it possible to implement something like withAjaxForbidden?
Note that obviously I expect the ajax system to be leaved in a consistent state in case an error is thrown.
Just create your own ajax function and make it dependent on some global variable. After that you can just remove the regular ajax functions from the scope and you're done :)
var ajaxEnabled = true;
function withAjaxForbidden(f){
ajaxEnabled = false;
f();
ajaxEnabled = true;
}
function ajax(...){
if(!ajaxEnabled)throw 'Ajax requests are forbidden within this block';
...
}
Here's a fiddle as an example: http://jsfiddle.net/u98b7bk3/1/
<div id="console">
No messages yet
</div>
<script type="text/javascript">
// Disable Ajax globally, to keep it working for your own library make
// sure you save this locally before overwriting
XMLHttpRequest = undefined;
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// Try an ajax request with jQuery
$.ajax('/').always(function(data, status, error){
$('#console').html("data: " + data + "<br>status: " + status + "<br>error: " + error);
});
</script>
Local variables override global variables within the function's scope. Simply create a variable for each native and library ajax function and set it to undefined from within your protected function. It will be prevented from being able to make use of the functions and variables by the same name. As long as they are set using "var" it should only affect the function's scope and not the global scope.
Edit: This may not be exactly what you were looking for, but as far as I know the precise way you want to accomplish this is not possible due to variable scoping but if you don't mind the extra code around your AJAX-restricted code you can use this method. Unlike the other answer this will work asynchronously and does not affect global variables, which makes it much less likely that problems will occur from the use of this code:
Edit 2 (with jQuery support):
var testFunction = (function($){
// Global to local/lexical:
var XMLHttpRequest = undefined;
var eval = undefined;
var setTimeout = undefined;
var setInterval = undefined;
var Function = undefined;
var window = undefined;
$ = (function($){ if($) {
var newjq = function(s, c) {
// Reroute main function
return $(s, c);
};
var jQueryBlacklist = {
// Initialize blacklist
"ajax": true,
"post": true,
"get": true,
"getJSON": true,
"getScript": true
};
for(i in $) // Reconstruct Object
if($.hasOwnProperty(i)
&& !jQueryBlacklist[i])
newjq[i] = $[i];
return newjq;
} }($));
// Real testFunction() below:
return function() {
// AJAX-forbidden code
// $.ajax should be undefined
}
}($));
// $.ajax should work normally here
testFunction(); // not in here
// $.ajax should work normally here
Click here to see the fiddle
Related
Original AJAX code with no JQuery. Note the definition of someVar at the very beginning.
req.onreadystatechange = function() {
if (req.readyState == 4) {
//CODE HERE
var someVar = ....
//method() should be called when AJAX call returns
someVar.method();
if (req.status != 200) {
// error code
someVar.doSomething();
} else if (req.responseText) {
//other code
someVar.doSomethingElse();
}
}
};
req.send(null);
My best attempt at JQuery. Note the code duplication:
$.get(url)
.done(function(data){
var someVar = ....
someVar.method();
someVar.doSomethingElse();
})
.fail(function(){
var someVar = ...
someVar.method();
someVar.doSomething();
});
Is there anyway to run code BEFORE done and fail (which is why always doesnt work in this case)?
Looking at the jqXHR documentation, done, fail, always and then should be invoked in the order they are registered - have you tried putting the shared code in an always before the other functions?
Here's a jsBin showing this in action; just be sure to leave the console open.
EDIT
This is a bit gimmicky, but if you're really deadset on not having a variable in the parent scope, there is some degree of shared context between the functions you can use. A jQuery ajax request is it's own object, and as such you can share data on this object between calls on it.
So, you could share the code like so:
$.get()
.always(function() {
this.someVar = ...
this.someVar.doMethod();
}).done(function() {
this.someVar.doneFunction();
}).fail(function() {
this.someVar.failFunction();
})
If you were to do this though, I'd be a bit more cautious with my variable conventions - probably would try to prefix someVar with something application-specific (like myApp_someVar).
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 have a script with the following structure:
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
},
init : function () {
// Call real init() function
$(document).ready(function () {
Test.FUNCTIONS.init();
});
}
};
$(document).ready(function () {
Test.init();
});
If I remove either of the $(document).ready(..) function calls, when I try to access a constant from a different namespace it is undefined; with both is works well.
As you can see I'm using two init() functions, one it just to neaten up the call to init because I have wrapped functions inside an additional object.
If I remove the function that is on the same level as CONSTANTS, VARIABLES etc and try to call the init() within Test.FUNCTIONS it still does not work.
Edit:
If i console.log(all_constants) I get the full object (with .chunk_of_markup) but if I console.log(tester) is get undefined. If i wrap tester i get []
I should also note that the other namespace gets the markup from a seperate file.
Any ideas why?
Having two document ready doesn't make a difference here. You could have one document.ready and/or call Test.FUNCTIONS.init directly and all should work, and the fact that they are in different namespaces doesn't matter as well.
As for why you're getting undefined, I think it is probably because your chunk_of_markup variable is actually undefined at that point. My guess is that you're getting the value for it through AJAX and so the call is done asynchronously which means the DOM will be ready before it actually returns a value. When you use the Debugger then the value is evaluated at the point of time where you run the command so by then, the async call already returns successfully (it's a race condition, if you're fast enough and your AJAX is slow then you can still get undefined, and it's also why 2 ready functions happen to make it slow enough for the AJAX call to return but it's still unreliable).
In all cases, if my theory is correct, then you need to hook to the callback of the AJAX request rather that DOM ready event, this is the only place where you can guarantee that your variable is defined.
Why not call the function init() in the document Handler itself.. I don't think that will lead to the same problems.. You can remove the Test.init() completely as it does not seem to do anything in here
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
}
};
$(document).ready(function () {
Test.FUNCTIONS.init();
});
Hi,
I have my main file in which I include my javascript file.
In my javascript file I have this
$(document).ready(function(){
//some functions here
});
I want all the functions just available to this page and I know you can kinda conceal them to outside world of javascript by doing something like
(function(){
$document.ready(function(){
//my functions
)};
}).init();
but I am not 100% sure how would it be called or whether its even the right way.
Anyone shedding light on this would be a great help!
In javascript everything declared inside a function is only available inside that function (except for when you declare a variable without the keyword var).
So everything inside the function that you pass to $().ready() is only available inside that function.
$(document).ready(function () {
//all code here is scoped inside this function, so it can't be accessed
// outside of this function
});
Like the first comment says you can't hide them from the user, if they really want to see it, they will see it.
You can clean them up in a way if you really wanted to, something like
var mySpace = {};
mySpace.init = function() {
// your init functions here
};
in doc ready you just call
mySpace.init();
I am not sure if this is what you wanted but it is the way I understood the question
(function(){
var secret1 = function(msg) {console.log("Secret Message:" + msg);}
$document.ready(function(){
secret1("this can only be called from within");
)};
})();
secret1("this will cause a script error");
It sounds like the thing you are looking for is a 'javascript obfuscator'. Here is an example one. It makes the code much harder to read and copy. But as others have said, you can't actually fully hide javascript.
The problem here is that JavaScript is intrinsically a client-side scripting language unless using a server-side javascript application such as node.js.
As long as JavaScript is being used in this way, the entirety of your code will be downloaded much like downloading a .txt file from a website. The only real difference is that the ".js" extension and its inclusion in html <script> tags or in an AJAX call will force the user's browser to render it as JavaScript.
If you want to make the script a little harder for the user to find, however, this is doable. I recommend having your website retrieve the script via AJAX and appending it to the DOM. You can do this with require.js or by using Kickstrap and making your script into an "app." The script won't appear as a link in the DOM and the user would really have to search for it. You can make it even more difficult (without compromising the integrity of your site) by minifying the script. This will make it run faster while inadvertently making it less human-readable on the front end.
In JavaScript there is only function scope (the exception argument in try-catch being an exception). ES5 will let you use let (no pun intended) to achieve block scope but it wont be usefull untill majority of UAs implement it.
So your functions are concealed from the outside world, if with outside you mean outside the dom ready event.
$( document ).ready( function () {
var myFunc = function () {};
} );
myFunc();// <- ReferenceError: myFunc is not defined
You can't really hide the functions, as it's in the source code of a file downloaded by the client, but you can make it so they can't access your functions from javascript.
(function() {
var doStuff = function() {
// Not Accessible
console.log('You can\'t reach me!');
}
return {
'init': function() {
// Accessible
doStuff();
}
}
})().init();
If you are talking about Access Modifiers like public, private etc. Then check out this article on how Javascript handles this. Here are the key components:
//constructor function (class)
function Maths(x, y) {
//public properties
this.x =x;
this.y = y;
//public methods
this.add = function () { _sum = x + y; return _sum; }
this.mod = function () { _mod = x % y; return _mod; }
//public method calls private method
this.show = function () {
this.add();
this.mod();
showResult();
}
//private variables
var _sum=0;
var _mod=0;
//private methods
function showResult() {
alert( "sum: " + _sum + ", mod: " + _mod );
}
}
//end function
//create instance
var plus = new Maths(3, 4);
plus.show();
//static method multiply, you can use it without instance of Maths
Maths.multiply = function (x,y) { return x * y; }
//call static method by constructor function (class) without instance of Maths
var result = Maths.multiply(5,7);
alert(result);
//output: 35
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.