I plan on using JSONP to call an external web service to get around the fact that I don't want to create a global function that could potentially conflict with the calling page. I thought that creating a random function name and passing it up would work. Something like this:
<script src="www.foo.com/b?cb=d357534">
where cb is the callback function name, the server would return
d357534({my json data});
What I want to know is how to create the random function name, I'm sure I could use eval but is this the best way to go about it?
Essentially, what I am trying to do is this:
var d + Math.floor(Math.random()*1000001) = function(){...
This should do what you want. You need to save the function name somewhere so that you can pass it to the server, but you can do that inside of a local scope to avoid polluting your global namespace.
var functionName = 'd' + Math.floor(Math.random()*1000001);
window[functionName] = function() { ... }
To make a randomly-named global variable you could do this:
window['randomvar' + Math.floor(Math.random()*1000001)] = function() { ... };
now of course you've got the problem of remembering the random name somewhere. You could make up a random name for that variable too. Then you'd have to remember the name of that variable, so that you could look at its value and then know how to find your function. After a while, things are going to start getting weird.
Why don't just use a counter and increment it each time you need a new function:
var name = "callback" + window.COUNTER++;
window[name] = function() { ... };
If you want to avoid littering the global namespace with too many references you could (and should) attach the counter and callbacks to a single global object:
var JSONP = window.JSONP;
var name = "callback" + JSONP.COUNTER++;
JSONP[name] = function() { ... };
In this case you could call the method like this:
JSONP.callback_12(json);
Of coarse you have to initialize the JSONPobject and the COUNTER variable first.
Related
I have 2 JS files - one with the functions I would like to access and the other that I'd like to call the function with.
(function($) {
var Place = function() {
var Location = function(id, duration, check) {
//Should access this function
}
}
})(jQuery);
I'm trying to access it with:
Place.Location(markerId, 600);
But all I'm getting is that it's not defined. Simple issue but can't quite figure this one out.
As it's a jQuery plugin, maybe there's a way I can access it via another method?
$.fn.place = function(params) {
var len = this.length;
return this.each(function(index) {
var me = $(this),
key = 'place' + (len > 1 ? '-' + ++index : ''),
instance = (new Place).init(me, params);
});
};
The way you are defining Location, it is a private variable inside the function Place. If you want to access it as an attribute of Place, you should replace var Location = ... with this.Location = ...
It's going out of scope. Because you wrapped your Place object in function($) {}, now anything outside that wrapper will no longer have access to variables inside the wrapper. If $ stands for jQuery, it should be a global anyways and you can take the wrapper out.
The solution is a combination of the other two answers.
You define Place as a variable in the (anonymous) function. It can't be used outside the scope of that function. (This function doesn't use jQuery, either, so the wrapper is unnecessary).
Place is a function. It executes code that sets local variable Location to a function, but doesn't export that function, so Location() is inaccessible outside the Place function.
You probably mean to make Place an object (instead of a function), and give it a Location method. Here's one way to write it:
var Place = {
Location: function(id, duration, check) {
// do something with id, duration, & check
}
};
// execute
Place.Location(someId, someDuration, someCheck);
(It doesn't look like you've posted all your code, like the Place.init() method, but there are plenty of ways to write this so that it works correctly; this should solve your immediate problem.)
I'm trying to call a js function within another one, but use the argument to specify the function. ie depending on the argument passed, it will call a different function
function toggle(n){
if (sessionStorage['toggle'+n]== 0){
check+n();
}
else
}
So, for example, if the argument 'Balloons' was passed as n, then it will call the function checkBalloons(). "check+n();" is not currently working here. Sorry for my lack of simple js syntax!
If the function is defined in the global scope (browser) you can do:
window["check"+n]();
or some tenants like Node.js you would access it from global object.
global["check"+n]();
if it is a part of some other object then you would do the same.
obj["check"+n]();
Functions and properties defined on an object can be accessed using [] convention as well. i.e obj["propFuncName"] will give you reference to it, so in case of methods you add () to invoke it.
If the function is global, you would do this:
window["check" + n]();
or, you could put your function in an object like so:
myNamespace = {};
myNamespace.checkSomething = function(){ /* ... */ }
// call it like this:
myNamespace["check" + n]();
The answers thus far are correct, but lack explanation.
In JavaScript, you cannot call a function by name when that name is a string. What you can do is retrieve a value from an object by name, and if that value happens to be a function, you can then call it. For example:
var myObject = {};
myObject.myFunction = function() { alert('test!'); };
// Get the property on `myObject` called `myFunction`
var theFunctionLookup = myObject['myFunction'];
// Since that property was a function, you can call it!
theFunctionLookup();
In the browser, functions that are defined in the global scope are attached to the window object. For example, this works:
function myFunction() { alert('test'); }
var theFunctionLookup = window['myFunction'];
theFunctionLookup();
You can shorten the last two lines into one:
function myFunction() { alert('test'); }
// Look up and call the function in one line.
window['myFunction']();
For the same reasons, you can use a dynamically-calculated string to look up functions.
function checkBalloon() {
alert('checking balloon');
}
function toggle(n){
if (sessionStorage['toggle'+n]== 0){
window['check' + n]();
check+n();
}
}
toggle('Balloon');
if you do this way:
if (sessionStorage['toggle'+n]== 0){
window["check" + n]();
}
will work
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.
Ok hopefully this come across correctly. I am building a universal javascript function that will build a menu and then also build the functions that each menu item would call. To do this, I need to pass a list of the commands to be called for each option.
So for example:
var thecall = 'alert("hi, this works");';
function myfunction(thecall)
{
//In here I want to excute whatever commands is listed in variable thecall
.....
}
I'm sure doing it this way is completely stupid, but I don't know how else to do this.
Basically, I need my function to perform other functions on a variable basis.
Thanks!!
I made it a bit fancier to show you how you can use it.
var thecall = function(name){alert("hi " + name + ", this works");};
function myFunction(function_ref)
{
function_ref('Mark');
}
myFunction(thecall);
You can execute arbitrary strings of JavaScript using eval(), but that is not the best solution for you here (it's almost never the best solution).
Functions in JavaScript are themselves objects which means you can store multiple references to the same function in multiple variables, or pass function references as parameters, etc. So:
var thecall = function() {
alert("hi, this works");
};
function myfunction(someFunc) {
someFunc(); // call the function that was passed
}
myfunction(thecall); // pass reference to thecall
Note that when passing the reference to the thecall function there are no parentheses, i.e., you say thecall not thecall(): if you said myfunction(thecall()) that would immediately call thecall and pass whatever it returned to myfunction. Without the parentheses it passes a reference to thecall that can then be executed from within myfunction.
In your case where you are talking about a list of menu items where each item should call a particular function you can do something like this:
var menuItems = [];
function addMenuItem(menuText, menuFunction) {
menuItems.push({ "menuText" : menuText, "menuFunction" : menuFunction });
}
function test1() {
// do something
}
addMenuItem("Test 1", test1);
addMenuItem("Test 2", function() { alert("Menu 2"); });
// and to actually call the function associated with a menu item:
menuItems[1].menuFunction();
Notice the second menu item I'm adding has an anonymous function defined right at the point where it is passed as a parameter to addMenuItem().
(Obviously this is an oversimplified example, but I hope you can see how it would work for your real requirement.)
I think your looking for the eval function.
var code= 'alert("hi, this works");';
eval(code);
Say I have some context where variables are set and a λ-function is called which uses them directly:
function outerContext(){
...
var data = ...; // some data the script uses
...
someObject.method = function(){
data; // the variable is used here
};
...
}
I know that the dynamically created function has a snapshot of the context it was created in, so data variable is accessible there.
What are the dangers I may face with such an approach when I use this dynamically created method? Should I always give this data as an argument or is it ok?
The inner function does not have access to a "snapshot", it has full access to the data variable.
function outer() {
var data = 1;
...
someObject.method = function () {
data = 42;
};
someObject.method();
// data == 42
}
(The real explanation being that when using data in the inner function, Javascript will try to figure out which scope data is in. It will traverse up the scope chain to find the place where the variable was created, and that's the variable that will be used.)
There's no "danger", this is one of the core competencies of Javascript. It's like an object method modifying an object's properties. Of course you need to take care what you want to do, do you really want to modify the variable or do you just want to use it locally?
For the "snapshot", you need to use a closure:
function outer() {
var data = 1;
...
someObject.method = (function (data) {
return function () {
data = 42;
}
})(data);
someObject.method();
// data == 1
}
I can't really think of any "dangers" besides the possibility of causing a circular reference and thus a memory leak in case of DOM objects or such.
It works much like a private variable in a class.