What is the difference of calling function like:
testCall: function() and function testCall() in jQuery ?
Update:
Questions: Does usage of one over the another have some performance issues related to it OR it really does not matter which one you are using ?
Update 2
Also other thing that I noticed that whenn I am defining function using testCall: function() and I call it using this.testCall() it works fine and am able to call it in any other function.
But when I am using function testCall() and I try to call it using testCall() in another function than I am getting errors and am not able to call it. Is this possible or there could be some other reason for the errors ?
In this example:
testCall: function()
testCall is now a function available on the object you're in, like this: object.testCall() It can access other functions, properties, etc inside this object if it needs to.
In this version:
function testCall()
testCall is just a globally available method, not scoped to the object or plugin, whatever you're dealing with, you can call it from anywhere, like this: testCall()
This is really a question about Javascript syntax (and semantics), not jQuery.
Both of those constructions define functions. This:
var x = {
// ...
name: function() { /* ... */ },
// ...
};
defines a function (an anonymous function) and assigns it as the value of the property called "name" in the object being assigned to the variable "x".
This:
function name() {
/* ... */
}
defines a function with the name "name". The effect is similar to:
var name = function() { /* ... */ };
but definitely different. However, for most purposes it's safe to think about them as being almost the same. The effect is that "name" is bound to the function in the lexically-enclosing scope. If you do that definition outside of any other function, then "name" becomes a property of the "window" object, and the function is therefore globally available. If that declaration is inside another function, then "name" is only available inside that function.
Usually you see the first form when you're doing something like setting up callbacks for some jQuery facility, like a UI plugin or $.ajax. You're giving jQuery a function that it should call upon something happening — an AJAX call finishing, or a use action like a mouse click, or completion of some sort of animation.
edit oh, and finally here's another note. If you define a function the second way, well then you can refer to that function by name and use it in an object definition (like the first example):
function globalFunction() {
function localFunction() { /* ... */ };
jQuery.something({
x: 100, y: 100,
callback: localFunction,
// ...
});
}
Many more such things are possible - functions are values in Javascript and can be tossed around as easily as numbers and strings.
The first (testCall: function()) is object literal notation for defining a function and assigning it to a property on an object (not shown). The function itself is anonymous; the property it is bound to has a name, but the function does not.
The second (function testCall()) is a named function.
Named functions have several advantages over anonymous ones, and so though you see the first format quite a lot, I would recommend using it sparingly if at all. Named functions can be reported usefully by your tools (debuggers and the like), whereas anonymous functions just show up as ? or (anonymous). More here.
For that reason, rather than this:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: function() {
// Do something involving the successful result and `foo`
foo.bar();
}
});
}
I would typically do this instead:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: niftySuccess
});
function niftySuccess() {
// Do something involving the successful result and `foo`
foo.bar();
}
}
Not only does this keep my code a bit cleaner (er, to my mind), but it means that if something goes wrong inside niftySuccess, I've given the function a name my tools can report to me. Note that other than the fact that the function has a name, they're identical – both functions are closures over the foo argument and anything else inside doSomeNiftyAjaxyThing.
You might be tempted to give the function a name inline, like so:
function doSomeNiftyAjaxyThing(foo) {
$.ajax({
url: "blah",
success: function niftySuccess() { // <== change here, PROBLEMATIC
// Do something involving the successful result and `foo`
foo.bar();
}
});
}
There you're declaring a function with a name as an expression, and assigning the result of the expression to a property. Arguably you should be able to do that, but there are a series of implementation...anomalies in the various Javascript engines out there that prevent your being able to do that. (More in the article linked above, and in this article.)
Related
I've seen EVERY example of how to replace anonymous functions with named ones. I'm looking for how a named function can be changed to an anonymous one. I'm looking to just optimize my code slightly. I understand how the anonymous function works, I just can't get the syntax right in this example.
Additionally, the doWork function is a big beast. I need it to stay named.
NOTE: I did google, and I'm either searching the wrong terms, or not a lot of people want to know how to do this. I humbly beg for SO's forgiveness of my failure to find this answer somewhere else.
NOTE2: Please ignore my use of closure with this.formFields. Just assume it won't change ever. I'm setting it at an earlier time.
My code:
function doWork(serviceResponse, theFormFields){
// Process stuff like jQuery or console test stuff etc
}
// THIS NAMED FUNCTION IS WHAT I WANT TO BE ANONYMOUS
function createCallback(formfields) {
return function(data) {
// This reference to the 'formfields' parameter creates a closure on it.
doWork(data, formfields);
};
}
// THE ABOVE FUNCTION *COULD* be anonymously declared in the getJSON
$.getJSON(jsonService + callString, createCallback(this.formFields));
$.getJSON(
jsonService + callString, // your param #1
(function (formField) { // here we create and execute anon function
// to put this.formFields into the scope as formField variable
// and then return required callback
return function (data) {
doWork(data, formField);
}
})(this.formFields)
);
I am trying to create a basic javascript framework that you can pass different things into, including functions for it to execute later. Right now, I'm in a more simple testing phase, but I can't quite get the function calling to work. A piece of my code is here:
[My JS Fiddle][1]http://jsfiddle.net/mp243wm6/
My code has an object that holds different data, and I want to call the method later, but with data that is available at the time of creation. Here is a code snippet of the function that uses the function that is passed to the object:
clickMe : function() {
this.obj.click(function() {
this.func();
});
}
Any suggestions or things I should read are welcome.
The problem is that there're two different contexts:
clickMe : function() {
// here is one
this.obj.click(function() {
// here is another
this.func();
});
}
You can simple pass the function as parameter, like the following:
clickMe : function() {
this.obj.click($.proxy(this.func, this));
}
http://jsfiddle.net/mp243wm6/2/
The problem:
Considering your code in the JSFiddle, you have:
onClick : function() {
this.obj.click(function() {
this.func();
});
},
As noted, you have different contexts going on here.
Consider the snippet this.obj.click(function() { this.func(); }). The first this here is a reference to the framework.events object. The second this here is a reference to whatever will be this when this function get called. In the case of your JSFiddle, when this.func gets called, this is actually the DOM object that represents the <div id="test">TEST</div> node. Since it doesn't have a func function, calling func() on it causes:
Uncaught TypeError: undefined is not a function
You have to understand the following: you have to pass the correct this in which you want the function func to be called.
The solution:
A couple of ways to make it work as you would like:
1. with bind
this.obj.click(this.func.bind(this));
This way, you are telling: "call my this.func function, but make sure that it will be called using the this that I am passing as a parameter". Vanilla JS, no $.proxy stuff.
JSFiddle
2. with a copy of the reference to the actual function
onClick : function() {
var theFunctionReference = this.func;
this.obj.click(function() {
theFunctionReference();
});
},
This way, you will not rely on the value of this outside of the context of the framework.events object.
JSFiddle
The issue is that this is not bound to the correct object. I would suggest you look into Function.bind() because that creates a function with this pointing to the right thing.
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.
The documentation of jQuery's hover shows only one method of using the function:
$('.myClass').hover(function () {
console.log('on mouse over');
},
function () {
console.log('on mouse out');
});
However, when you change these to named functions it doesn't work correctly, firing the named functions upon page load (or as soon as you paste it into your console):
function onMouseOver() {
console.log('on mouse over');
}
function onMouseOut()
console.log('on mouse out');
}
$('.myClass').hover(onMouseOver(), onMouseOut());
Changing the last line to:
$('myClass').hover(onMouseOver, onMouseOut);
works as expected (firing on the event), but doesn't allow me to pass anything to the named functions. Is there any way to allow me to pass a variable to the functions?
Yes you need to use anonymous functions for this:
$('myClass').hover(function( e ) {
onMouseOver( param1, param2... );
}, function( e ) {
onMouseOut( param1, param2... );
});
You can pass variables into the named functions by calling it like so:
$('.myClass').hover(function() {
onMouseOver(arg);
}, function() {
onMouseOut(arg);
});
That's the only way to pass arguments, parameters into the named functions from that event.
The hover sugar method isn't really meant for complex scenarios.
In your case it would probably be better to use .on('mouseenter') and on('mouseleave') so that you can pass additional event data to each method, like
$('.myClass').on('mouseenter', {param1: val1}, onMouseOver).on('mouseleave', {param2: val2}, onMouseOut);
Then within your handlers you can access those params like so:
function onMouseOver(e) {
console.log(e.data.param1);
}
function onMouseOut(e) {
console.log(e.data.param2);
}
That's the sort of jQuery way to do it.
This is a problem with function references vs. function invocation. Adding the "()" invokes the function (which in this case you'd be doing at binding time...effectively binding the result of the function rather than the function itself.
To pass arguments the simplest option would be to wrap the named function in an anonymous function (as #antyrat just posted).
And also no, this is not a quirk of hover, this is standard JavaScript (and most any other language that has first class functions).
As several others have noted, you'll have to use currying or binding to pass values to the functions. In the example where you wrote this:
$('.myClass').hover(onMouseOver(), onMouseOut());
you're actually calling the onMouseOver() and onMouseOut() methods immediately on that line, and not when the mouse actually moves over or out of the element; what you wrote is equivalent to writing this:
var mouseOverResult = onMouseOver();
var mouseOutResult = onMouseOut();
$('.myClass').hover(mouseOverResult, mouseOutResult);
That's definitely not what you want.
jQuery can only understand functions that are of the form function(event), so if you want more parameters, or other parameters, you'll have to use currying to get them in there. Currying (named for the math professor who devised the concept) can be thought of as creating a new function where the values you want to pass are 'pre-bound' inside it.
So let's say you have a variable foo that you'd like to pass into your onMouseOver handler, like this:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
$('myClass').hover(...);
To be able to pass that value, you need another function that wraps up that foo and that onMouseOver with a function signature that jQuery can use. You do it like this:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
var curriedOnMouseOver = function(event) {
onMouseOver(foo);
};
$('myClass').hover(curriedOnMouseOver);
As several others have suggested, you can avoid the extra variable declaration by creating the curried closure inside the hover() call:
function onMouseOver(foo) {
...
}
...
var foo = "Hello, World";
$('myClass').hover(function(event) {
onMouseOver(foo);
});
This example above also shows how you would pass the event to your function as well, by simply adding more parameters to it:
function onMouseOver(event, foo, bar) {
...
}
...
var foo = "Hello, World";
var bar = "Goodbye, World";
$('myClass').hover(function(event) {
onMouseOver(event, foo, bar);
});
JavaScript's functions --- or closures --- are incredibly powerful tools, and it would be worth your while to learn some of the things you can do with them, like these examples.
How do I make the myFunction visibile for the in-line function in .ready() event?
$(document).ready(function() {
...stuffs...
myFunction(par1, par2, anotherFucntion_callback);
}
);
function anotherFunction_callback(data) {
..stuffs..
}
I didn't quite catch your question. Do you mean that you want to pass "myFunction_callback(data)" as the last argument in your:
myFunction(par1, par2, anotherFunction_callback);
, including that "data" parameter?
In that case the solution is pretty standard, write this before that one:
var temp = function() { anotherFunction_callback(data) };
an alternative syntax is:
function temp() { myFunction_callback(data) };
// even though this looks just like a free function,
// you still define it inside the ready(function())
// that's why I call it "alternative". They are equivalent.
In general, if you want to pass a function with 1 or more arguments to another function, you use that format. Here, we basically create a new no-argument function that calls another. The new function has access to the "data" variable. It's called "closure", you may want to read more on that.
Of course, if the callback require no argument, you can just use the original function name.
I hope this helps.
ps: You can even inline the function declaration, making it anonymous, like so:
myFunction(par1, par2, function() { myFunction_callback(data) });
Notice that the
$(document).ready(function() {});
looks pretty much just like that.
You use the actual name of the function, i.e. myFunction_callback instead of myFunction or anotherFucntion_callback.