I have a function that would use other variables, depending on what has been passed.
Like this = ActionBar(slot) slot contains "one".
and I would like to create a call inside that like object.slot.name but it should convert it before hand to make the command look like object.one.name. Is there a way to do this in javascript/jquery?
I remember vaguely that some other language does this as {slot} or something like that.
Sorry if this question was already asked, I've checked google and stackoverflow too, but didn't find an answer.
Also I'd like to know what's the proper programming term for this kind of variable passing?
Edited it cause of misunderstandings. I'm looking into OOP js, so object is an object, one is an object, and name is an attribute, but when passing I'm passing "one" as a string to the function.
Tried eval, it doesn't work while dotted with an object.
Source code:
function disableActionButton(slot){
$("#"+slot).attr("disabled","disabled")
gcd = player.slot.gcd*1000
cd = setInterval(function(){
gcd = gcd - 10
$("#"+slot).val(gcd+"ms").css("color","red");
},10)
setTimeout(function(){
window.clearInterval(cd)
$("#"+slot).removeAttr("disabled").css("color","black").val(player.slot.name);
}, player.slot.gcd*1000)
}
It's really unclear what your current structure is (much clearer now you've posted code, see "update" below), but fundamentally the way to do this sort of thing in JavaScript is to have a container object. (If you don't already have one, introduce one.) Then slot can refer to a property of that object, like this:
var container = {
one: "This is one",
two: "This is two"
};
// ...
function foo(slot) {
console.log(container[slot]);
}
// ...
foo("one"); // ends up logging "This is one"
foo("two"); // ends up logging "This is two"
This works because the container object has properties, which in JavaScript can be referred to in two different ways:
Using dot notation and a literal name, e.g. container.one, or
Using bracketed notation and a name in a string, e.g. container["one"].
They're exactly equivalent except where the property name comes from. And of course, in the second case, the property name needn't be a literal string, it can be the result of any expression, including a variable reference (e.g., you can get the name from slot).
This works with all object properties, including properties that refer to functions. I mention this only because you mentioned functions in your question, so if you need to, you can do this:
function foo(slot) {
container[slot]();
}
...which calls the function on container that the property with the name held by the slot argument. So if slot is "one", it does container.one().
Update:
Your source directly echos the container example above, just apply the above to it:
function disableActionButton(slot){
$("#"+slot).attr("disabled","disabled")
// ---------v----v---- here
gcd = player[slot].gcd*1000
cd = setInterval(function(){
gcd = gcd - 10
$("#"+slot).val(gcd+"ms").css("color","red");
},10)
setTimeout(function(){
window.clearInterval(cd)
// ------------------------------------------------------------ and here--v----v
$("#"+slot).removeAttr("disabled").css("color","black").val(player[slot].name);
// ------v----v------- and here
}, player[slot].gcd*1000)
}
Or, rather than looking up the slot data each time, grab it once and reuse it:
function disableActionButton(slot){
// Grab it once...
var slotdata = player[slot];
$("#"+slot).attr("disabled","disabled")
// ---vvvvvvvvv--- then use it
gcd = slotdata.gcd*1000
cd = setInterval(function(){
gcd = gcd - 10
$("#"+slot).val(gcd+"ms").css("color","red");
},10)
setTimeout(function(){
window.clearInterval(cd)
$("#"+slot).removeAttr("disabled").css("color","black").val(slotdata.name);
}, slotdata.gcd*1000)
}
There's no special name for this. You're passing a property name into a function, and the function is looking up the property on the player object using that name. In some other languages this might be called "reflection" but the term doesn't really apply to dynamic languages like JavaScript.
Related
I am trying one of the examples from the book 'Java Script the Good Parts'. Find below the code.
It throws an error saying Number has no method integer. Could you please help me understand the problem.
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Number.method('integer', function () {
return Math[this < 0 ? 'ceiling' : 'floor'](this);
});
After running the above code, it attaches a method integer to Number.prototype
Number.prototype.integer
It displays the function.
Number.integer
It says undefined.
Number.integer(10);
I get - Number() { [native code] } has no method 'integer'
I am not sure where I am going wrong.
I tried by replacing ceiling with ceil. Still it does not work.
You're adding the method to the prototype assigned to Number instances, you're not adding it to the Number function itself.
So to use it, you call the method on an instance of Number, like this: Live Example | Live Source
var n = 3.1415;
var i = n.integer();
console.log("i = " + i); // "i = 3"
Or with a numeric literal: Live Example | Live Source
console.log(3.1415.integer()); // "3"
console.log((3).integer()); // "3"
Note that in the second line above, I had to put the 3 on its own in parens; otherwise, the JavaScript parser thinks the . is a decimal point rather than a property accessor.
Side note: Extending the prototypes of predefined types with enumerable properties can be a bit hazardous. I've never seen any significant problems when people extend Number or String, but there are major problems if you extend Array or Object, so I would stay away from doing that.
Side note 2: In my examples above, I've also corrected the name of Math.ceil (it's ceil, not ceiling).
The method function (which is taken from Javascript the Good Parts) which you are adding to the prototype of Function helps you define a method on given class or type so that all instances or objects of that type inherit that method.
Two problems in your code:
You are calling integer as if it were defined on the class or constructor function Number itself which is not the case, it is defined on the prototype of Number. If the function was actually defined on Number itself, it would have been defined like this:
Number.integer = function() { /* Implementation */ };
You are passing a parameter to the function (i.e. Number.integer(10)), although in the body of the function you aren't dealing with that number, nor you have declared the integer function to accept any parameters. I guess you are confused that this number is being refereed to by the this keyword inside the function but that is not correct.
The method function allows you to define a method on a given type and you can benefit from it like this:
var a = 10.3; // Now a is a Number instance and so it inherits the integer() method
console.log(a.integer()); // call the method on a
You can even call it directly on literals and they will be wrapped automatically by the interpreter, like this:
console.log((32.543).integer());
I'm trying to sort out if this is plausible but have gotten syntax errors at best. So I am wondering if it is at all possible.
What I have is an object (example only)
var myObj = {
something1_max:50,
something1_enabled:false,
something1_locked:true,
something2_max:100,
something2_enabled:false,
something2_locked:true,
something3_max:10,
something3_enabled:true,
something3_locked:true
}
and what I want to do through a function is do something like again for example to sum things up..
function displayDetails(theScope, obj)
{
console.log(obj.[theScope]_max);
}
(function(){displayDetails('something3', myObj);})()
so when displayDetails() is called whatever the scope I can see in this example the max for that scope. In the console log for the example I would hope to see 10
Properties of JavaScript objects can always be accessed as a string using the bracket syntax, ie object['property']. This, of course, means you can build that string dynamically:
console.log(obj[theScope + '_max']);
Put the property name string in brackets.
console.log(obj[theScope + '_max']);
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.
I have an object:
var object = {
string1 : 'hello',
string2 : 'world'
}
And If I want to call the string2 property of the object is it slower to call it multiple times like:
...object.string2...
...object.string2...
or it would be faster to create a reference for it what holds the value of the parameter like:
var string2 = object.string2;
...string2...
...string2...
The reason why I think that the second one could be faster, because I think right now that the first one always scans the whole object to grab the value.
You are correct - the second one is faster, because JavaScript does not need to perform the lookup of string2 each time. The change is even more profound in something like this:
(do stuff with foo.bar.baz.qux)
versus
var property = foo.bar.baz.qux;
(do stuff with property)
In that example, foo must be scanned for bar. Then bar must be scanned for baz. Et cetera.
In your example the gain will be minimal unless you are doing a lot of work with string2, but you are correct in saying that it is faster.
In you case this does not matter . But for big object with big prototype chunks - you right.
But you may win in speed but loose in functionality because var a = obje.property coping by value - not by reference and if obj.property will be change dynamically that variable a will have old value of obj.property
i want to create a dynamic generated form using javascript, everything works fine, until i try to pass an array as parameter. When i do this, an error happens. Coulr anyone explain what this is?
Heres my code:
var loadFrm = function(component) {
for(nItem in component) {
var myComponent = "add" + firstToUpper(component[nItem].type);
var callComponent = myComponent + "(" + component[nItem].opt + ");";
eval(callComponent);
}
}
var json = [
{
type: "scale",
opt: {content: [{label: "male", value: "m"}, {label: "female", value: "f"}]}
}
];
loadFrm(json);
Edit Here's the error:
missing ] after element list
[Break on this error] addScale([object Object]);
If you use a debugger to look at the string callComponent, you'll probably find it looks something like this:
addScale([object Object])
...which isn't what you want. That's because you're effectively calling toString on your opt object, and the default toString on objects just looks like that. The eval error is because that's invalid syntax.
Generally speaking, any time you think you need to use eval, there's almost certainly a better answer. In this case, it looks like you're trying to call a function and pass in opt. Assuming these functions are "globals", you can do that like this:
var loadFrm = function(component) {
var nItem, functionName;
for (nItem = 0; nItem < component.length; ++nItem) {
functionName = "add" + firstToUpper(component[nItem].type);
window[functionName](component[nItem].opt);
}
}
Live example
Notes on the above:
Don't use for..in to loop through arrays unless you really know what you're doing. for..in does not enumerate the indexes of an array, it enumerates the properties of an object.
We look up the function by name using window[functionName]. This works because "globals" are actually properties of the window object, and you can look up properties using a string name for them using bracketed notation.
Having gotten the function via window[functionName], we just call it directly, passing in the object opt rather than a string form of it. I assume addScale expects to see an object.
I moved all of the vars to the top of the function because that's where they really are (details).
If you can, I'd recommend moving addScale and the related functions to their own object rather than putting them on window. The window namespace is already pretty crowded. Here's the live example modified to not add any symbols to window at all, instead putting the addScale function on an object called functions and using it from there.
Off-topic: The syntax var loadFrm = function(component) creates an anonymous function that it then assigns to a variable. This is used a lot, but unless you're creating different functions based on a condition, e.g.:
var f;
if (...) {
f = function() { ... };
}
else {
f = function() { ... };
}
...it's not actually useful. (If you are creating different functions based on a condition like that, then it's not only useful, it's necessary.) I recommend using named functions whenever possible, because a function with a name helps your tools help you by showing you the function name in error messages, call stacks, etc.
Off-topic 2: You have a variable called json, but FYI, it's not using JSON notation. It's using a combination of JavaScript array and object literal notation, which is a superset of JSON. You'll see a lot of people confused about this, I mention it because you said you're new and so it's worth nipping in the bud. :-) JSON is purely a notation. (A very useful one.)
Use this:
fn = eval(functionName);
fn(objParameter)