Here's the problem - I know function by name (and that function has already been loaded form an external script), but I don't have an actual function object avail for me to call. Normally I would call eval(function_name + "(arg1, arg2)"), but in my case I need to pass an object to it, not a string.
Simple example:
var div = document.getElementById('myDiv')
var func = "function_name" -- this function expects a DOM element passed, not id
How do I execute this function?
Thanks!
Andrey
Never use eval, it´s evil (see only one letter difference)
You can simply do:
var div = document.getElementById('myDiv');
var result = window[function_name](div);
This is possible because functions are first class objects in javascript, so you can acces them as you could with anyother variable. Note that this will also work for functions that want strings or anything as paramter:
var result = window[another_function_name]("string1", [1, "an array"]);
You should be able to get the function object from the top-level window. E.g.
var name = "function_name";
var func = window[name];
func( blah );
Related
I would like to produce a Javascript object which contains a property (onClick example) of type function (myClickHandler example) like this:
var options = {onClick: this.myClickHandler};
I want the object above to be created from a string because the options object can be different every time the app runs and I want it to be evaluated during runtime. (myClickHandler is an existing function). I want the options object created from a string because onClick property can be something else and its function can be something else also. These are determined from the string which is dynamic.
Looking for something like this:
var optionsString = "{onClick: this.myClickHandler}";
var options = JSON.parse(optionsString); //won't work. For illustration only.
This won't work naturally. Ideally, I want to convert the string in one go but I might have to parse it but optionString can contain one or more properties.
Try using eval as :
var myClickHandler = function(){ alert('i am evil'); };
var options = JSON.parse('{ "onClick": "this.myClickHandler" }');
// calling handler now
eval(options.onClick)();
You need some context where the function is bound to, so that you can access the function by key (which can be a string).
let context = {};
context.myclickhandler = () => {
console.log("### It clix ####")
}
let nameOfFunction = "myclickhandler"
let obj = {"onClick" : context[nameOfFunction]};
obj["onClick"]();
In the example the context is just an object , but it could also be the windows object or a instance of a constructor function.
When I do:
var x = $("#listing")
I get back html element with id listing,
And when I do $(x) or $($("#listing")), I get the same.
What is difference b/w two?
$() will convert something to a jQuery object (or collection). This is not the same as a Javascript variable.
When you store #listing in a variable such as var x = '#listing', you are simply passing a string to the jQuery constructor, which is then interpreted as a selector by Sizzle, jQuery's selector engine.
In the example provided, there is no difference between the two following lines:
var x = $('#listing');
var x = '#listing',
$x = $(x);
In the first snippet, x is identical to $x in the second.
In the interest of completeness, the jQuery constructor can also accept a mixed type variable as its first parameter; it doesn't have to be a string. For example, it's possible to convert a DOMElement variable into a jQuery object using the following syntax:
var ele = document.getElementById('myItem'),
$ele = $(ele);
Notice that $ele now has access to jQuery's own functions, such as addClass(), etc. Please see this demo.
Furthermore, passing a jQuery object to the constructor will simply return the same jQuery object. For example, given the following snippet:
var $x = $('#listing'),
$x2 = $( $x );
$x is identical to $x2.
Your x variable was made a jQuery object once it found the dorm item.
Once you run var x = $('#listing'); x has everything wrapping it has.
Thus you can run x.addClass('thing')
Adding $ is creating jQuery object, its not normal variable. You can create jQuery object from DOM element, from another jQuery object or from normal javascript variable. Try to run console.log(x) console.log($(x)) and it will tell you all differences.
I'm trying to create a simple JS that will get random arrays and post them via div container. Here's what I have so far, now keep in mind I'm horrible with JS.
function timedMsg() {
currMsg++;
document.getElementById('timedMsgDiv').innerHTML = msgArr[currMsg % msgArr.length];
};
function init() {
currMsg = -1;
msgArr = Array('Computer.', 'Uploading', 'Random');
timedMsg();
var t = setInterval("timedMsg()", 50);
};
window.onload = init();
It only gets 'Computer' and then does not flip through them randomly or loop or anything. Any reason why? Here's my JSFiddle: http://jsfiddle.net/R8SYf/
Pass the function name to setInterval(), like this:
var t = setInterval(timedMsg, 2000);
http://jsfiddle.net/R8SYf/6/
There are a few problems in your code:
1) When you pass a string to setInterval you trigger eval and your function is run in the global context, you don't want this. To fix it simply pass a reference to the function; rebember functions are objects too:
var t = setInterval(timedMsg, 2000);
2) The window.load event expects a function but you're assigning the returned value of the init function which is undefined. Like in the case above you need to pass a function object, not execute the function:
window.onload = init;
3) Your fiddle is set-up to load the code in the onLoad event, but you're assigning your own function. You need to change the behavior to No wrap - in <head>.
4) Although is works, using the Array constructor to create arrays is not good practice. Simply use the literal syntax:
msgArr = ['Computer.', 'Uploading', 'Random'];
Fixed http://jsfiddle.net/R8SYf/11/
Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.
The following works:
$ = document.form;
x = $.name.value;
This doesn't:
$ = document.getElementById;
x = $("id").value;
Any ideas on why this doesn't work or how to make it so?
The value of this depends on how you call the function.
When you call document.getElementById then getElementById gets this === document. When you copy getElementById to a different variable and then call it as $ then this === window (because window is the default variable).
This then causes it to look for the id in the window object instead of in the document object, and that fails horribly because windows aren't documents and don't have the same methods.
You need to maintain the document in the call. You can use a wrapper functions for this e.g.
function $ (id) { return document.getElementById(id); }
… but please don't use $. It is a horrible name. It has no meaning and it will confuse people who see it and think "Ah! I know jQuery!" or "Ah! I know Prototype" or etc etc.
The context object is different. When you get a reference of a function you're changing that context object:
var john = {
name : "john",
hello : function () { return "hello, I'm " + this.name }
}
var peter = { name : "peter" };
peter.hello = john.hello;
peter.hello() // "hello, I'm peter"
If you want a reference function bound to a specific context object, you have to use bind:
peter.hello = john.hello.bind(john);
peter.hello(); // "hello, I'm john"
So in your case it will be:
var $ = document.getElementById.bind(document);
Don't know what you want to achieve, but this can be made working like this
$ = document.getElementById;
x = $.call(document, "id").value;
because getElementById works only when it is a function of document because of the scope it needs.
But I would recommend #Quentin's answer.
getElementById is a method of the HTMLDocument prototype (of which document is an instance). So, calling the function in global context you will surely get an "Wrong this Error" or something.
You may use
var $ = document.getElementById.bind(document);
but
function $(id) { return document.getElementById(id); }
is also OK and maybe better to understand.
If you are trying to achieve something like that I would suggest using jQuery. Their $ notation is much more powerful than just getting an element by id.
Also, if you are using any platform that already uses the $ as a variable (ASP .Net sometimes uses this) you may have unpredictable result.