可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I was looking through new stuff added to jQuery 1.7 and I saw they now have jQuery.Callbacks() http://api.jquery.com/jQuery.Callbacks/.
The documentation shows you how to use jQuery.callbacks() but not any applicable examples of when I would want to use them.
It seems you can add/remove callbacks from a callbacks list and you can do jQuery.callbacks().fire(args), but this just fires off ALL of the callbacks in that list. Maybe I am missing something but this doesn't seem very useful.
In my head when I first saw this new functionality I thought you would be able to use it with key/value pairs. Which would then provide a simple way to manage callback functions in a single place in your application. Something like
$.callbacks.add("foo", myFunction);
and then for example if I wanted to call that callback at the end of my function I could do something like
$.callbacks().fire("foo", args);
However it doesn't look like you can fire off specific callbacks, you can only fire off all of them with the given arguments or none of them.
The closest thing I saw was being given the ability to give the .fire() function a context to set the "this" property
.fireWith(context, args)
but this doesn't really help much either.
Am I misunderstanding the documentation?
If this is the desired functionality what are some applicable examples where this is useful.
回答1:
To expand on @Rockets answer a bit and clear up some confusion:
The reason that one might need to use jQuery's $.Callbacks
is multifaceted:
- The user has a lot of code in one function and wants to split it up
They take that information and send it through the jQuery callback function which then allows them to have split their code into better manageable pieces with which to work with.
So (for example) if you look at @Rocket's code:
var clickCallbacks = $.Callbacks();
clickCallbacks.add(function() { //one one function piece
//parse and do something on the scope of `this`
var c = parseInt(this.text(), 10);
this.text(c + 1);
});
clickCallbacks.add(function(id) { //add a second non-related function piece
//do something with the arguments that were passed
$('span', '#last').text(id);
});
$('.click').click(function() {
var $ele = $(this).next('div').find('[id^="clickCount"]');
clickCallbacks.fireWith($ele, [this.id]); //do two separate but related things.
});
- What you can now have is multiple callback batches of function which you can now call whenever you deem it be necessary without the need to make so many changes throughout out your code.
回答2:
I can see callbacks being useful when you are updating different DOM elements using the same method(s).
Here is a cheesy example: http://jsfiddle.net/UX5Ln/
var clickCallbacks = $.Callbacks();
clickCallbacks.add(function() {
var c = parseInt(this.text(), 10);
this.text(c + 1);
});
clickCallbacks.add(function(id) {
$('span', '#last').text(id);
});
$('.click').click(function() {
var $ele = $(this).next('div').find('[id^="clickCount"]');
clickCallbacks.fireWith($ele, [this.id]);
});
It updates the click counter and the 'last clicked' when you click on something.
回答3:
An (almost) out of the box jQuery Pub/Sub system
This is IMHO the most interesting application, and since it was not clearly stated in the answers (although some do make allusion to the usage), I am adding it to this relatively old post.
NB: The usage is clearly indicated, with an example, in the jQuery docs, but I guess it was added after the question was posted.
Pub/sub, aka the observer pattern, is a pattern which promotes loose coupling and single responsibility in an application. Rather than having objects calling directly the methods of other objects, objects instead subscribe to a specific task or activity and are notified when it occurs. For a more detailed explanation of the benefits of using the Pub/Sub pattern, you can check Why would one use the Publish/Subscribe pattern (in JS/jQuery)?.
Sure, this was possible with custom events using trigger
, .on()
and .off()
, but I find jQuery.Callbacks
to be a lot more suitable to the task, producing cleaner code.
Here is the example snippet from the jQuery documentation:
var topics = {};
jQuery.Topic = function( id ) {
var callbacks,
method,
topic = id && topics[ id ];
if ( !topic ) {
callbacks = jQuery.Callbacks();
topic = {
publish: callbacks.fire,
subscribe: callbacks.add,
unsubscribe: callbacks.remove
};
if ( id ) {
topics[ id ] = topic;
}
}
return topic;
};
And a usage example:
// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );
// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );
// Here, "hello world!" gets pushed to fn1 and fn2
// when the "mailArrived" notification is published
// with "woo! mail!" also being pushed to fn1 when
// the "mailSent" notification is published.
/*
output:
hello world!
fn2 says: hello world!
woo! mail!
*/
回答4:
It seems that $.Callbacks
began as an implementation detail: a means to manage lists of functions and to call all the functions in a given list with the same arguments. A little like C#'s multicast delegates, with additional features, like the flags you can pass to customize the list's behavior.
A good example might be that jQuery uses $.Callbacks
internally to implement its ready
event. bindReady()
initializes a callback list:
readyList = jQuery.Callbacks( "once memory" );
Note the once
and memory
flags, that ensure the callback list will only be called once, and that functions added after the list has been called will be called immediately.
Then, ready()
adds the specified handler to that list:
ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();
// Add the callback
readyList.add( fn );
return this;
}
Finally, the callback list is fired when the DOM is ready:
readyList.fireWith( document, [ jQuery ] );
All the ready
handlers are called in the context of the same document with the same reference to the global jQuery object. They can only be called this once, and additional handlers passed to ready()
will be called immediately from then on, all of this courtesy of $.Callbacks
.
回答5:
I don't see any specific mention of setting context, but since you can pass an arbitrary number of arguments, that would potentially be useful. You could also make your own convention to pass a flag as a first argument and have listeners return false immediately if they aren't meant to handle the remaining argument list.
I've encountered cases where something like this might have been useful, but have used bind() and trigger() with custom events instead. Imagine some message handling system (a web based chat room or e-mail client) where you're polling a service for new messages. One function might be setting a number in a span or displaying a growl when something happens. Another might be updating a grid. With triggers you would have to trigger the event for each listener and "unroll" the passed arguments from eventData, with callbacks it's just one fire and your listeners are simple javascript functions with a simple argument list.
Callbacks isn't exactly revolutionary, but it'll make for less and cleaner code.
回答6:
I'm working on an app with a lot of business logic and at least 11 external services. It really helps keep things straight if you can write your own flow control classes and behaviors using something like Callbacks instead of trying to force your will on the Deferred implementation.