WelcomeMy columnView a series of articles.
To be honest, the source code of sizzle is so depressing that it was very painful during the period of writing sizzle articles. At first, I thought it was very interesting. The later I felt that the code was difficult to read and annoying.
After the winter vacation, I stayed at home for two weeks. I felt good. During this period, I forgot everything I learned and learned very little. I finished watching CCTV’s general history of China series. It is also a kind of peace of mind for me who is a history fan.
When today’s topic is away, sizzle pauses for two weeks and feels a lot sober. Most of the pain previously dominated by sizzle has been eliminated. Today, let’s introduce the callbacks function of jQuery.
Use of callbacks
JQuery internally provides many methods for basic functions, such as $ ajax()、$. Each () and $ Callbacks (), these methods can be used internally or externally by developers.
There are several main methods supported by callbacks, such as add, fire, remove and disable. For example, there is an official example:
//These two are used as the callback function
function fn1( value ) {
console.log( value );
}
function fn2( value ) {
fn1("fn2 says: " + value);
return false;
}
//Call jQuery's callbacks to generate callbacks
var callbacks = $.Callbacks();
callbacks.add( fn1 );
callbacks.fire( "foo!" );
// 'foo!'
callbacks.add( fn2 );
callbacks.fire( "bar!" );
// 'bar!'
// 'fn2 says: bar!'
As can be seen from the basic demo,$.Callbacks()
Function generates a callbacks object whose.add()
The method is to add a callback function, and.fire()
The method is to execute the callback function.
.remove()
To remove the callback function:
var callbacks = $.Callbacks();
callbacks.add( fn1 );
callbacks.fire( "foo!" );
// 'foo!'
callbacks.add( fn2 );
callbacks.fire( "bar!" );
// 'bar!'
// 'fn2 says: bar!'
callbacks.remove( fn2 );
callbacks.fire( "foobar" );
// 'foobar'
$.Callbacks()
Several parameters are also supported to indicate several effects of callback execution,$.Callbacks('once')
:
-
once
: make sure that this callback list only executes Fire() once (like a deferred deferred) -
memory
: keep the previous value, add the latest value to the back of this list, and call any callback immediately (like a deferred deferred) -
unique
: ensure that only one callback can be added at a time (so there are no duplicate callbacks in the list) -
stopOnFalse
: interrupt the call when a callback returns false
This method also supports multiple parameters, such as$.Callbacks('once memory')
Please refer to this for specific uselink。
Callbacks source code
Before putting the source code of jQuery 3.0, let’s simply simulate the callbacks function to realize its basic functions:
var Callbacks = function(){
var Cb = {
callbacks: [],
add: function(fn){
this.callbacks.push(fn);
return this;
},
fire: function(value){
this.callbacks.forEach(function(fn){
fn(value);
});
return this;
}
}
return Cb;
}
//Testing
var callbacks = Callbacks();
callbacks.add(fn1);
callbacks.fire('test'); //'test'
You can see that a simple callbacks function is very simple to implement.
The whole callbacks source code is roughly as follows:
jQuery.Callbacks = function(options){
//First process the parameters, such as once and unique
options = createOptions(options);
//Parameter definitions, including some flag and callback arrays
var list = [], queue = [] ...
//Fire is to traverse the array and return the execution of the function
var fire = function(){
...
}
//Self is the final returned object
var self = {
add: function(){...},
remove: function(){...},
has: function(){...},
disable: function(){...},
fireWith: function(){...},// This is actually the execution of the fire function
fire: function(){...}
...
}
return self;
}
Because we have briefly introduced how to implement a basic callback function, here is a little clearer. Let’s take a lookcreateOptions
Function, which is mainly used for functions similar to$.Callbacks('once memory')
Type flag separation for callback:
function createOptions(options) {
var object = {};
jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) {
object[flag] = true;
});
return object;
}
amongrnothtmlwhite
Is a regular expression/[^\x20\t\r\n\f]+/g
To get all the flag flags. The result of createoptions is an object, and the key values are flag and Boolean respectively.
So the main problem now is all on those flags,"once memory unique stopOnFalse"
。
Source code:
jQuery.Callbacks = function(options) {
//Flag processing
options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options);
var // Flag to know if list is currently firing
firing,
// Last fire value for non-forgettable lists
memory,
// Flag to know if list was already fired
fired,
// Flag to prevent firing
locked,
// Actual callback list
list = [],
// Queue of execution data for repeatable lists
queue = [],
// Index of currently firing callback (modified by add/remove as needed)
firingIndex = -1,
// Fire callbacks
fire = function() {
//Only execute it once, and it will not be executed in the future
locked = locked || options.once;
// Execute callbacks for all pending executions,
// respecting firingIndex overrides and runtime changes
fired = firing = true;
for (; queue.length; firingIndex = -1) {
memory = queue.shift();
while (++firingIndex < list.length) {
//The callback executes the function, checks for stoponfalse, and prevents it from continuing
if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) {
// Jump to end and forget the data so .add doesn't re-fire
firingIndex = list.length;
memory = false;
}
}
}
// Forget the data if we're done with it
if (!options.memory) {
memory = false;
}
firing = false;
//Locked is implemented here
if (locked) {
//Although it is locked, it is memory. Keep the list for future use
if (memory) {
list = [];
//Bye
} else {
list = "";
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if (list) {
// If we have memory from a past run, we should fire after adding
if (memory && !firing) {
firingIndex = list.length - 1;
queue.push(memory);
}
(function add(args) {
jQuery.each(args, function(_, arg) {
if (jQuery.isFunction(arg)) {
if (!options.unique || !self.has(arg)) {
list.push(arg);
}
} else if (arg && arg.length && jQuery.type(arg) !== "string") {
// Inspect recursively
add(arg);
}
});
})(arguments);
if (memory && !firing) {
fire();
}
}
return this;
},
// Remove a callback from the list
remove: function() {
jQuery.each(arguments, function(_, arg) {
var index;
while ((index = jQuery.inArray(arg, list, index)) > -1) {
list.splice(index, 1);
// Handle firing indexes
if (index <= firingIndex) {
firingIndex--;
}
}
});
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function(fn) {
return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0;
},
// Remove all callbacks from the list
empty: function() {
if (list) {
list = [];
}
return this;
},
// Disable .fire and .add
// Abort any current/pending executions
// Clear all callbacks and values
disable: function() {
locked = queue = [];
list = memory = "";
return this;
},
disabled: function() {
return !list;
},
// Disable .fire
// Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions
lock: function() {
locked = queue = [];
if (!memory && !firing) {
list = memory = "";
}
return this;
},
locked: function() {
return !!locked;
},
// Call all callbacks with the given context and arguments
fireWith: function(context, args) {
if (!locked) {
args = args || [];
args = [context, args.slice ? args.slice() : args];
queue.push(args);
if (!firing) {
fire();
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith(this, arguments);
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};
return self;
};
Generally speaking, the code of this pub / sub mode is easy to understand. There are some questions. For example, there are actually two arrays in the source code. List is a queue array, which should be called queue, but the queue array has been defined, and the function of queue is to store parameters during fire execution. This can not be confused.
In addition, when the whole code fires this parameter, the queue element needs to be supplemented when the function is running, that is, when fire is executed twice, butfire()
The function executes only once.
summary
jQuery. Callbacks follows the usual routine of jQuery, and finallyreturn self
, when I first saw it for the second time, it was a little vague. It was mainly the once, memory and other flag parameters that interfered with my line of sight, especially the implementation of these flag flags.
reference resources
jQuery. Callbacks Chinese document
JQuery 2.0.3 source code analysis callback object – callbacks
This paper is based on GitHubSource addressWelcome to star.
WelcomeMy blogcommunication.