JQuery\Javascript - Passing a function as a variable - javascript

I was just curious if I could pass a function as a variable. For example:
I have a function
$('#validate').makeFloat({x:671,y:70,limitY:700});
I would like to do something like this:
$('#validate').makeFloat({x:function(){ return $("#tabs").offset().left+$("#tabs").width();},y:70,limitY:700});
This does not work, but ideally every time the variable was accessed it would compute the new value. So if the window was resized it would automatically adjust as opposed to a variable passed in being static. I realize I can implement this directly inside the function\widget, but I was wondering if there was some way to do something like the above.
The concept of this is independent of the plugin. I am talking about the function being "cast" as a variable.

Yes, you can pass an object which will invoke some function when its property is read (this is called a getter), but it is not cross-browser compatible. For example, this will (probably) work in IE9:
var o = {y:70, limitY:700};
Object.defineProperty(o, 'x', {get: function() {return 671;}});
$('#validate').makeFloat(o);
There are other syntaxes for other browsers such as __defineGetter__ for Firefox, and some browsers don't have this functionality at all. So it is practically useless unless you can fully control the environment where your code runs.

This won't work unless x is invoked (obj.x(), instead of just obj.x).
To make it work, the makeFloat() code must check the type of x, and if it's a function, invoke it.

I see what you're trying to do, but it won't work. Why? makeFloat expects the value to be non-function type. It probably uses that value directly. To actually execute the function, makeFloat needs to do x() or even x.call(...) or x.apply(...), which it most certainly isn't doing.
To answer your other question i.e., can you pass functions as variables, the answer is yes. In fact, this is the way callbacks and closures are handled in Javascript. For example, in jQuery when you bind an event handler you are passing in a function as a parameter:
jQuery("#myInputId").click(function() {
...
...
});
Another way that parameters are passed in are as object attributes, for example in jQuery.ajax:
jQuery.ajax({
...
success: function(data) {
},
...
});
In both cases, click and ajax both understand and expect the parameter to be a function and not just a regular variable. For example, assuming you had an object that maintained a list of integers and you had a method called addElement(int), which expected an int parameter, you wouldn't pass in a String. It works the same way in Javascript, except for the fact that the language is not strongly typed. This is why you don't really get a type-mismatch error unless the function explicitly checks the type and throws an exception. This is generally a good practice in such language; I try to do this in the Javascript code that I write.

I've done this with string variables. You'll need to exploit the toString function.
function RefString(fn) { this.toString = function() { return String(fn()); }; }
You can use it like so:
$("#someDiv").somePlugin({optionValue: new RefString(MyFunc), ... });
function MyFunc() {
return new Date().getYear().toString();
}
It works by setting optionValue to a new OBJECT, not necessarily a function. Then anything that reads this object will ask for a value, which by default is the result of the toString function. We simply override the default behavior by executing a function that is specified when the object is constructed.
I'm not sure how it will work for EVERY plugin, but it works when a string or number is expected.

How do you mean "doesn't work"?
It looks like it should compile and run. But what happens is it executes the function and sets the value no different than if you used a constant, or called a function that wasn't inline.
What you need to do is put this line of code in an event that fires when the window is re-sized.

It looks like makeFloat is from a jQuery plugin - are you sure that the plugin is aware that 'x' can be a function and will execute it properly? From the jQuery site, it looks like it only is able to comprehend a number value or 'current' as a string, not a function.

You can pass functions as variables, yes - but that's not actually what you're asking.
What it looks like your asking is "can I set a DOM property to the result of an expression?" to which the answer is "no". (Note - not outside of browser-specific behavior such as IE's CSS Expressions - which have been deprecated in IE8 anyway)
You'll need to bind an event handler to window.onresize and use a function to update the sizing yourself.

In order for a function to be executed from a variable, it has to be called, like so:
$.option.callback.call();
Where option is the containing variable, callback is the function and call executes the function.
It's not like you don't have options though. You can set it up so that the returned value of that function is executed from the line itself. Or you can set it up in the alternative manner that you described.

You need to invoke that function so that it returns the actual value you're looking for. So you're not actually passing in a function, you're invoking it and it's immediately returning a value. For example:
$('#validate').makeFloat({
x:function(){
return $("#tabs").offset().left+$("#tabs").width();
}(),
y:70,
limitY:700
});
Notice the extra () after the function call. This invokes the function immediately, thus returning the value you're looking for.

Note that x doesn't "compute new value" when is accessed (read), but when the function is called, i.e. x(). As Chad mentioned, this is how you can automatically execute a function when windows is resized:
$(window).resize(function() {
// do something
});
[Update] After re-reading your question, I think you may be thinking overcomplicated – isn't this what you are looking for?
$('#validate').makeFloat({
x: $("#tabs").offset().left + $("#tabs").width(),
y: 70,
limitY: 700
});

Related

What JavaScript concept allows for the same name to be both a function and an object?

I've been using JavaScript for a few years now in web design/development, and everything I know has been self-taught (I was a design major and hobbyist front-ender turned full web developer in pursuit of my career). With that background, I discovered something that I want to learn more about and have no idea what it is called, how it works, or that it may even be something extremely simple.
I've tried to search for more information about this (to prevent myself from needing to ask this), but it's difficult when I'm not sure what it's called that I'm even looking for...
I noticed in a few JavaScript libraries that I use that a variable name can be both a function and an object. For example, the jQuery library uses the name "jQuery". When logged using typeof jQuery it is a function, and typeof jQuery() is an object. What is interesting to me is that initial thought would suggest that jQuery() would be a function but it's actually an object.
//jQuery:
ƒ (a,b){return new r.fn.init(a,b)}
//jQuery():
r.fn.init {} //Expanding this reveals functions inside of __proto__ (which is obviously a clue, but I need more info)
When I try to do something like this I end up overwriting the name (as I would expect):
//Declare an object:
var planetEarth = {
'sky': 'blue',
'grass': 'green'
}
//Now overwrite the object as a function:
function planetEarth(){
console.log('inside of a function now');
}
This is understandable within the realm of JavaScript, so my question is how does a library like jQuery pull off having both at the same time?
Ultimately (using the above example) I would like to be able to do something like this:
planetEarth().sky; //"blue"
So my roundabout question is simply what is this called?
And a follow-up of where can I learn the basics of accomplishing this?
I've found resources on object-oriented JavaScript and classes, as well as prototypes, but I'm not sure if either (or both) of those concepts are what this is. All of the articles I've found aren't starting at the beginning and seem to always jump into unnecessarily complex examples. I'm not looking to make a giant library- I just want to get some more experience at the very basic level. Again, this could be embarrassingly simple and I've just never come across the concept before, so I appreciate being pointed in the right direction, thanks.
Every JavaScript function is also an object, just as every array is also an object. I don't think there is a special name for this, it's just how the language works.
You may be confusing two different things: what a function returns when you call it, vs. the function itself. Take your example:
planetEarth().sky; // "blue"
This code does not rely on the function planetEarth being an object and having any properties. You're calling the function, so to make this code work the function would need to return an object.
Because a function is an object, you can also set properties directly on the function itself. The code below uses both of these features. This version of planetEarth() returns an object with sky and grass properties, so it works as in your example: you call the function to get an object with those properties.
The code also sets an exists property directly on the function, and you can access that property without calling the function:
function planetEarth() {
// Return an object when this function is called
return {
sky: 'blue',
grass: 'green'
}
}
// Set a property directly on the function itself
planetEarth.exists = true;
// Test accessing the function's property
console.log( planetEarth.exists );
// Test accessing a property in the object that the function returns when called
console.log( planetEarth().sky );
jQuery makes use of both of these facilities. jQuery is a function that you can call. It returns a value commonly called a "jQuery object". That object has properties and methods, such as jQuery('#foo').show(). It also has "array-like" properties that you can access as you would any array, e.g. jQuery('#foo').length and jQuery('#foo')[0]. The jQuery function adds those properties to the value it returns.
At the same time, the jQuery library adds other properties and methods to the jQuery function itself. You access without calling the function, e.g. jQuery.ajax({...}).
A couple of other notes to help you understand the jQuery code. First, download the uncompressed version of jQuery. It looks like you are studying the compressed version, which has shortened names that won't make any sense.
Also, if you are wondering what jQuery.fn is, it is simply an alias for jQuery.prototype. Whenever you see jQuery.fn you can mentally substitute jQuery.prototype; learn how JavaScript prototypes work and you will understand jQuery.fn.
And now you may wonder why jQuery.fn exists at all. Why not use jQuery.prototype directly like other JavaScript code that uses prototypical inheritance? In fact, you could, and it would work the same as jQuery.fn. But the very first version of jQuery, back in 2006, didn't work this way. jQuery.fn was its own separate object, and every time you called the jQuery function, it copied all of the methods and properties from this object into the value it returned.
As you can guess, this was rather inefficient, so jQuery was changed to use .prototype so it could return an object that inherits all the methods such as .show() and .hide() instead of copying them all one by one. At the same time, jQuery.fn was kept around as an alias for jQuery.prototype to avoid breaking existing code.
This is a silent answer...
function f() {
return {};
}
console.log(typeof f, typeof f());
This is how jQuery does it. f is a function but when it gets called it returns an object.
Interesting part: function is also an object. f instanceof Function and f instanceof Object are both valid. So, you can call a variable as a function and / or assign some properties because it is also an object.
f.test = 123;
First-Class Objects
In Javascript, functions are first-class objects. This means that functions are just another kind of object. You can put a function in a variable, you can return a function, you can make an array of functions, and all that. Functions are objects.
Consider a slight change in your attempt:
//Declare a function:
function planetEarth(){
console.log('inside of a function now');
}
// Now add fields to it, since it is also an object
planetEarth.sky = 'blue';
planetEarth.grass = 'green';
// Test stuff
planetEarth(); // works
console.log(planetEarth.sky, planetEarth.grass); // works
You mention that you would like to use planetEarth().sky, but observe that while planetEarth is a function (and an object, as I said), planetEarth() is the result of calling planetEarth with no parameters. Therefore, whether you can or can't do planetEarth().sky does not depend on planetEarth as an object having the sky field, but rather depends on whatever you return from planetEarth having that field.
Bonus: did you know that functions can be declared very much like "normal" variables? See below:
// Both lines of code below are identical:
function myFunc() { ... }
var myFunc = function() { ... };
Perhaps looking at the code above will help you clear the confusion. The function is myFunc. myFunc() is simply the act of calling that function. If typeof myFunc() gives function, it is just a coincidence that the object that myFunc returned happened to also be a function.
jQuery is a function. Properties can be assigned to a defined function.
function planetEarth(options){
console.log('inside of a function now', options);
return window["planetEarth"];
}
var planetEarthOptions = {
'sky': 'blue',
'grass': 'green'
}
for (let prop in planetEarthOptions) {
planetEarth[prop] = planetEarthOptions[prop];
}
window["planetEarth"] = planetEarth;
planetEarth("selector");
console.log(planetEarth.sky);
console.log(planetEarth().grass);

Can I find a JQuery function inside some object defined into the JavaScript window global object?

I am pretty new in JavaScript and JQuery and I have the following doubt.
I know that if I open the FireBug console typing window I see the content of the global object that in a JavaScript application that run into a browser is the current browser tab (the window).
Ok, so in this global object I can see all the global variables and all the global functions (the variables and the functions that are not defined inside another function).
So I have a page in which I link my .js file.
This file will contain some pure JavaScript function like this:
function myFunction() {
.................................
.................................
.................................
}
And so I will see the myFunction function as a field of the window global object because this function is global (it is not defined inside another function). This is perfectly clear to me.
So, into the .js file linked inside my page I also have something like this:
$(document).ready(function() {
$("#myButton").click(function() {
............................................
............................................
DO SOMETHING WHEN THE BUTTON HAVING id="myButton" IS CLICKED
............................................
............................................
});
});
So, this is a JQuery code and it should work in this way (correct me if I am doing wrong assertion).
There is the $ that is the JQuery objet (or what is it?).
On this JQuery object I call the ready() function that is the function that perform its inner callback function when the DOM is completly rendered.
So the inner callback function contain the:
$("#myButton").click(function() {...DO SOMETHING...});
the select a button having id="myButton" and add to it the click event listerner that itself define an inner callback function that is performed when the button is clicked.
Is it true?
Ok...so I think that all these stuff is not direcctly in the global object because it is not directly defined into my .js file but have to be in some memory space dedicate to JQuery.
So looking inside the window object inside the FireBug console I found two objects:
$: that I think is the JQuery object...so I think that my previous custom JQuery function have to be here but I can't find it.
JQuery: that is another object that is inside the window global object.
So, my doubt is: when, inside the ready() function I declare something like
$("#myButton").click(function() {...DO SOMETHING...});
where I can find the definition of the function() {...DO SOMETHING...} inside some object defined inside the window global object? Can I find it? Or am I missing something?
jQuery stores its event-related data in a data object events applied to each element. You can use $._data() to grab this info:
$._data($('#myButton')[0], 'events')
or
$._data(document.getElementById('myButton'), 'events')
To get the callback function that you applied for your button's click listener, you can simply grab the handler. For example:
$("#myButton").click(function () { console.log('clicked'); });
var eventsInfo = $._data(document.getElementById('myButton'), 'events');
console.log(eventsInfo.click[0].handler);
The above should print out "function () { console.log('clicked'); }".
Keep in mind that there is no public documentation available for $._data(), although it is a neat thing to know!
The following blog post mentions $._data() when jQuery v1.8 was released but does warn about this:
Note that this is not a supported public interface; the actual data structures may change incompatibly from version to version.
That was back in 2012. To this day, it seems to be working fine with the latest 1.x and 2.x versions, so I don't see this going away anytime soon.
This is an anonymous function, basically a piece of unique code that you don't really want to write a named function for.
It's a one-time use-case or rather it's used to bind custom click events without littering the global object with variables.
What an anonymous function does is the exact opposite of what you are asking since you can't find it in the global object (anonymous function).
.click is a function defined in $.fn and since $ is part of the window object you could traverse there to find click e.g. window.$.fn.click would be the path to the source of $(...).click(func...) but an anonymous function is a function that gets set and then forgotten (more or less).
After all, you're not giving it a name so there is no reference it can point to which is exactly what this is.
If you use a named function as an argument to another function it's called a callback function
An anonymous function is basically a nameless callback function, a callback function is a normal function that can be passed to other functions as a callable argument - this normal function will then internally use .call() or .apply() to execute the supplied callback which is what jQuery for instance does when you bind a click
The good thing here is that you're not missing anything at all, as a matter of fact - you're asking the right question because this will look like magic if you're just starting out but once you get the hang of it it's easy to understand and use (and misuse so be careful!)
If you'd like to know how this construction works you could always build your own function that accepts a callback / anonymous function e.g.
function result_based_on_callback(a, b, fn) {
fn.call(null, a, b);
}
The above function takes two parameters and a function, it will call the function and supply the two parameters to it (the null is the context of this which is a different kind of question :))
If we were to use the above construct to do a calculation we could do so like this:
console.log(result_based_on_callback(1, 2, function(a, b) { return a + b; }));
This would return 3, you can also do this with a normal function that would otherwise take two numbers and add them - it works the same except for just passing in the function name rather than the body
like this:
function do_add(a, b) {
return a + b;
}
console.log(result_based_on_callback(1, 2, do_add));
Which will do the exact same.
I hope this allows you to understand a bit of how this works, good luck!

why is it necessary to wrap function call in a function body

I often see something like the following in JavaScript:
$("#sendButton").click(function() {
sendForm();
}
Why is it necessary to wrap the call to sendForm() inside a function? I would think that doing it like this would be more readable and less typing.
$("#sendButton").click(sendForm);
What are the advantages/disadvantages to each approach? thanks!
There's typically two cases where you'd want to use the former over the latter:
If you need to do any post-processing to the arguments before calling your function.
If you're calling a method on an object, the scope (this reference) will be different if you use the second form
For example:
MyClass = function(){
this.baz = 1;
};
MyClass.prototype.handle = function(){
console.log(this.baz);
};
var o = new MyClass();
$('#foo').click(o.handle);
$('#foo').click(function(){
o.handle();
});
Console output:
undefined
1
Probably one too many answers by now, but the difference between the two is the value of this, namely the scope, entering sendForm. (Also different will be the arguments.) Let me explain.
According to the JavaScript specification, calling a function like this: sendForm(); invokes the function with no context object. This is a JavaScript given.
However, when you pass a function as an argument, like this: $(...).click(sendForm), you simply pass a reference to the function for later invocation. You are not invoking that function just yet, but simply passing it around just like an object reference. You only invoke functions if the () follows them (with the exception of call and apply, discussed later). In any case, if and when someone eventually calls this function, that someone can choose what scope to call the function with.
In our case, that someone is jQuery. When you pass your function into $(...).click(), jQuery will later invoke the function and set the scope (this) to the HTML element target of the click event. You can try it: $(...).click(function() { alert(this); });, will get you a string representing a HTML element.
So if you give jQuery a reference to an anonymous function that says sendForm(), jQuery will set the scope when calling that function, and that function will then call sendForm without scope. In essence, it will clear the this. Try it: $(...).click(function() { (function() { alert(this); })(); });. Here, we have an anonymous function calling an anonymous function. We need the parentheses around the inner anonymous function so that the () applies to the function.
If instead you give jQuery a reference to the named function sendForm, jQuery will invoke this function directly and give it the scope that it promises to always give.
So the answer to your question becomes more obvious now: if you need this to point to the element target of the click when you start work in sendForm, use .click(sendForm). Otherwise, both work just as well. You probably don't need this, so skip the anonymous function.
For those curious, scope can be forced by using the JavaScript standard apply or call (see this for differences between the two). Scope is also assigned when using the dot operator, like in: obj.func, which asks of JavaScript to call a function with this pointing to obj. (So in theory you could force obj to be the scope when calling a function by doing something like: obj.foo = (reference to function); obj.foo(); delete obj.foo; but this is a pretty ugly way of using apply.
Function apply, used by jQuery to call your click handler with scope, can also force arguments on the function call, and in fact jQuery does pass arguments to its click handlers. Therefore, there is another difference between the two cases: arguments, not only scope, get lost when you call sendForm from an anonymous function and pass no parameters.
Here you are defining an anonymous event handler that could call multiple functions inline. It's dirty and tough to debug, but people do it because they are lazy and they can.
It would also work like your second example (how I define event handlers):
$("#sendButton").click(sendForm)
Something you get by defining your event handlers inline is the ability to pass event data to multiple functions and you get this scoped to the event object:
$("#sendButton").click(function(event) {
sendForm();
doSomethingElse(event);
andAnotherThing(event);
// say #sendButton is an image or has some data attributes
var myButtonSrc = $(this).attr("src");
var myData = $(this).data("someData");
});
If all you are doing is calling sendForm, then there isn't much difference, in the end, between the two examples you included.
$("#sendButton").click(function(event) {
if(event.someProperty) { /* ... */ }
else { sendForm({data: event.target, important: 'yes'}); }
}
However, in the above case, we could handle arguments passed to the callback from click(), but if the sendForm function is already equipped to handle this, then there's no reason why you wouldn't place sendForm as the callback argument if that is truly all you are doing.
function sendForm(event) {
// Do something meaningful here.
}
$("#sendButton").click(sendForm);
Note that it is up to you where you handle the differing layers of logic in your program; you may have encapsulated certain generic functionality in a sendForm function then have a sendFormCallback which you pass to these sorts of function which handle the interim business of event/callback processing before calling sendForm itself.
If you are working in a callback-heavy environment, it would be wise to separate significant functionality from the callback triggers themselves to avoid callback hell and promote maintainability and readability in your source code.
It's just to lock scope. When you wrap that sendForm() in the anonymous function that closes over the current scope. In other words, the this will be kept with it. If you just pass sendForm then any calls to this will come from the calling scope.
This is a good question for learning about scope in javascript, and questioning conventions.
Nope, that second example is perfectly valid.
99.9% of jQuery examples use the first notation, that doesn't mean you need to forget basic JavaScript syntax.

Weird issue with "this" losing its value in a validation function in aspect.around. Or, aspect.around is not working as expected

Note: please keep in mind that this is not a generic question on the use of this on javascript. This is about aspect.around malfunctioning (it's meant to set the scope for the call, and it doesn't). The question is: why is aspect.around malfunctioning? this question needs you to read carefully how to reproduce and do so with the fiddle provided!
I had to shred my app to pieces in order to make the problem fit in a fiddle.
So here it is:
http://jsfiddle.net/mercmobily/THtsv/1/
It's a simple form, with validation:
Type something in the textbox: the validation method of the widget will be called.
Then press the submit button: validation will fail, and aspect.around will be called to wrap something around the validation method.
At that point, try to type anything in the textbox again: It will come back with an error, as the validator will fail because of "this" being set to "window" instead of the widget.
So, once the aspect is added, the validator stops working. Basically, the value of "this" gets lost. Now:
aspect.around() is meant to run the new validator in the right scope (obviously) and it's failing to do so
I can "fix" this problem by changing the call to the validator into this: return originalValidator.call(this, value); However, it doesn't answer the question "Why is 'this' lost?"
If you backtrace the code, you will see that aspect.around() is doing what it normally does... but it must be doing something wrong
So, the question: why is dojo.around() malfunctioning, not setting this to the passed object's scope?
Merc.
It is not very easy to follow what exactly you're asking. From your jsFiddle, I see this comment so I'll attempt to answer the question you pose here:
// QUESTION: FIND OUT WHY WE NEED THIS "call"
return originalValidator(value);
// return originalValidator.call(this, value);
The answer to why you need the .call here in order to preserve the value of this is as I described below in the generic description of how this works when making a function call.
When you make an ordinary function call as in this statement:
return originalValidator(value);
The value of this is set back to window. That's how javascript works. If you want to preserve the current value of this in that function, you have to specify that you want a particular value of this set using .call() or .apply() or an obj.method() call. The value of this in an ordinary function call is NOT bound to the function. It's set by the caller and can be anything the caller wants. If you don't specify it, then javascript sets this to window and that is exactly what is happening in your code.
Here's the generic description of how the value of this is set and this generic description applies in your specific case.
The simple rule is that the value of this is reset on every single function call in javascript. If it's just a plain function call, then this is set to the global object (which is window in the browser environment). So any simple function call will always set this to window.
If you make a method call like obj.method(), then this will be set to point to the obj while in the method().
If you use func.apply(a, b) or func.call(a, b) then you can explicitly control what this is set to via the value of the first argument to .apply() or .call(). See this MDN doc here or here for more info on .call() and .apply().
this is the current context. By default it's the global object (or null in strict mode), when calling a function on an object (foo.bar()) it's set to that object. When using .call() or .apply() to call a function it's set to whatever first argument was passed to that function.
This means that you cannot assume that this is still the same when you go into another function - even if you define that function in a context where this is what you want.
The most common approach is adding var self = this; and then using self instead of this in the inner function - since self is a normal variable it will be in the function's closure and not be affected by this being bound to something else in the function.

Best approach to avoid javascript's “this” mistakes in XBL

Talking about XBL is not exactly talking about javascript. So I'll create this question that's related to this one, but now about XBL, where I'm not the creator of the root javascript code, but just of the methods and event handlers inside the bindings.
--
In some cases, the this keyword may not refer to the object I expect it to. (recent example: in an key event, in my XBL)
What's the best approach to avoid this kind of mistake?
For now, I'm using always the getElementById (or $.fn from jQuery), but I'm not sure if it's the best approach.
--update
A little bit more details:
Within a XBL method the only way to access the element that was defined in the Xul file (the GUI description file) without using "this" (as it may not be the "this" I expect) is with getElementById, and this makes the code not reusable, so I'm looking for alternatives..
As you've heard elsewhere, the problem is that the this parameter isn't necessarily the object you first thought of, especially when you're talking about event handlers and the like. The best way I found is to write a small function that will bind some object as the this variable and return another function that uses the binding to call the original function.
Take a look at this code:
var bindFunction = function(self, f) {
return function() {
return f.apply(self);
};
};
var foo = function() {
alert(this.hello);
};
var bar = {
hello: "Hello there"
};
var boundFoo = bindFunction(bar, foo);
boundFoo();
What I have is a function called bindFunction that takes an object (called self) and some function, and returns another function that will call the passed-in function applying the self object as the this variable.
The rest of the code is just some test code: a foo function that will alert this.hello, a bar object with a hello member, and how you can bind foo with bar.
In essence, I've taken a function that accepts no parameters and generated another function that will ensure the first is always called with the correct this variable. Ideal for event handlers.
(Sorry, but I know nothing about XBL, so I hope this will be of use.)
If there's not a best answer, a good approach could be use javascript files instead of XBL for the implementation. Looking the Xulrunner source code, looks like most code does this.
But I still would like to use XBL, if there was a way to work fine with it..

Categories

Resources