Let's start with how callbacks is used: the first example
function A () {}
Function B () {}
var cb = $. Callbacks ();
Cb.add (a);
Cb.add (b);
Cb.fire (); //You will execute the a method first and then execute the B method
The above probably means: The Add method is to add the method to the array list and add a method each time it is executed. The fire method is to execute the methods in the list array sequentially.
Use scenario: second example
function A (n) {}
(function () {
function B (n) {}
})
You cannot call the B method here, because the B method is a local variable. Therefore, you can use callbacks to solve this problem.
function A (n) {}
var cb = $. Callbacks ();
Cb.add (a);
(function () {
function B (n) {}
Cb.add (b);
})
Cb.fire ("Hello");
At this point, the B method can be invoked by Cb.fire () even if it is a local variable. Also, the parameter "Hello" in fire can be passed into the A,b method.
Callbacks can pass 4 values, jquery.callbacks (options), options have four values: Once,memory,unique,stoponfalse.
In the first example, suppose we call two times cb.fire (); The A,b method is executed for the first time, and the A,b method is invoked the second time. But if you define CB this way, var cb = $. Callbacks ("once"); then only the first time the A,b method is executed, and the second is not performed. Once means that only the methods in the list array are executed once, and then you call fire and do not execute the methods in the list array.
The role of memory is:
var cb = $. Callbacks ();
Cb.add (a);
Cb.fire ();
Cb.add (b);
For the above code, method a executes and the B method does not execute. But when you define CB, var cb = $. Callbacks ("Memory"); At this point, the A,b method will execute. It has a memory.
Unique role: Go heavy, add if you add the same method, add a few of the same methods in the absence of a unique case, you will execute the same method several times, but you pass in the unique, no matter how many times you add the same method, it will only execute once.
The role of Stoponfalse:
function A () {return false;}
Function B () {}
var cb = $. Callbacks ();
Cb.add (a);
Cb.add (b);
Cb.fire ();
Method A,b will be executed. But if you define CB, var cb = $. Callbacks ("Stoponfalse"); only the A method is executed. Because the A method returns false, the function of Stoponfalse is to stop the loop list when the method returns false. So the B method is not executed.
Of course these four kinds of parameters, you can combine use, such as: var cb = $. Callbacks ("Once Memory");
Next, source analysis: Because the code is more, so give everyone a clue, $. Callbacks-> return self; -> Self.add (a)-> List.push (a)-> Self.add (b)-> List.push (b)-> self.fire ()->self.firewith-> private Method F IRE ()-> the loop list array to execute the A,b method inside. Look at the code in this order, and then say what you mean by 4 parameters, and then look at it in order. There's a detailed code explanation inside.
var optionscache = {};
function createoptions (options) {
var object = optionscache[Options] = {}; //optionscache[once] = {}
Jquery.each (Options.match (Core_rnotwhite) [], function (_, flag) {
Core_rnotwhite =/\s+/g, take a character, Options.match (core_rnotwhite) = ["Once"], this is to handle multiple values, such as "once Match" into ["Once", "memory "]
object[flag] = true; //_ is the index value 0,flag is the value "once"
});
return object; Optionscache[once] = {"Once": true}
}
Jquery.callbacks = function (options) {//options has four values: Once,memory,unique,stoponfalse
options = typeof options = = "string"? (optionscache[options] createoptions (options)): Jquery.extend ({}, Options);
When the argument is passed, it is a string, such as: once, then the Optionscache is taken to the once attribute, and if not, the createoptions (once) method is invoked
Var memory,
fired,
Firing,
Firingstart,
Firinglength,
Firingindex,
list = [],
stack =!options.once && [], //if there is once, then St The ACK is false, if not, then stack dies [the empty array in the],if condition is true, so when fire ()->firewith (), the method in the list array can be executed again.
Fire = function (data) { /This is where the method in the list array is actually executed
memory = opt Ions.memory && data;
fired = true;
Firingindex = Firingstart 0;
Firingstart = 0;
Firinglength = list.length;
firing = true; is triggering a method in the list array
for (; list && fir Ingindex < Firinglength; firingindex++) {
if (list[firingindex].apply (data[0], data[1]) = = False && op Tions.stoponfalse) {
If the callback method returns false and there is an incoming stoponfalse parameter, the For loop is stopped, and the following method is stopped from executing the list array
Memory = false;
Break
}
}
firing = false; Method Execution End
if (list) {//When the method execution in the list array is finished, the stack is judged to have a value
if (stack) {
if (stack.length) {
Fire (Stack.shift ()); If there is a value, the fire execution is triggered again
}
else if (memory) {//If Once,stack is false, it will go else
list = []; If a memory is passed in, only the list array is emptied. For example: var cb = $. Callbacks ("Once Memory"); Cb.add (a), Cb.fire (); Cb.add (b); Cb.fire (). Because there is a once, it only executes once, so the second fire is useless. And because of the memory, the A,b method is executed once. If there is no memory, the following else statement is executed, only the A method is executed and the B method is not executed.
} else {
Self.disable (); Block all fire actions that follow
}
}
},
Self = {
Add:function () {//This Add method is the push method in the list array (the method called when fire)
if (list) {//[] is true
var start = List.length;
(function add (args) {
Jquery.each (args, function (_, arg) {
var type = Jquery.type (ARG);
if (type = = "function") {//If the method is pushed into the list
if (!options.unique!self.has (ARG)) {//If there is a unique judgment, such as passing in unique, you must determine if there is a method in the list array, and if not, push to the list. The Self.has (Arg) method is to determine whether there is an Arg method in the list array.
List.push (ARG);
}
else if (Arg && arg.length && type!== "string") {
Add (ARG); Add ([A,b]) now executes method add instead of Self.add.
}
});
}) (arguments); Here we deal with Add (a,b), add ([A,b]), and add multiple methods at the same time
if (firing) {
Firinglength = List.length;
else if (memory) {//The first time you call add, memory is undefined. When you call fire, if you pass in the memory, then memory becomes true, and then you call add and you go to the IF statement to fire. So after you pass in the Memory,fire, call Add (b) and the B method will execute.
Firingstart = start;
Fire (memory);
}
}
return this;
},
Remove:function () {//Remove the corresponding method from the list array
if (list) {
Jquery.each (arguments, function (_, arg) {
var index;
while (index = Jquery.inarray (ARG, list, index)) >-1) {
List.splice (index, 1); Mainly through the splice method of array to remove
if (firing) {
if (index <= firinglength) {
firinglength--;
}
if (index <= firingindex) {
firingindex--;
}
}
}
});
}
return this;
},
Has:function (FN) {//jquery.inarray (FN, list), if FN returns 1 in the list, does not return-1
RETURN FN? Jquery.inarray (FN, list) >-1:!! (list && list.length); If you do not pass in a value, you determine if there is a value in the list, and a value returns TRUE.
},
Empty:function () {
list = [];
firinglength = 0;
return this;
},
Disable:function () {//All subsequent operations are invalid
List = stack = memory = undefined;
return this;
},
Disabled:function () {
return!list;
},
Lock:function () {///will only lock the fire operation behind and let the Cb.fire method fail.
stack = undefined;
if (!memory) {
Self.disable ();
}
return this;
},
Locked:function () {
return!stack;
},
Firewith:function (context, args) {///This executes the method in the list array
if (list &&!fired stack) {//fired is undefined the first call, the second call, fired true, depends on stack.
args = args []; If a value is passed in the fire, it is args, and if it is not, it is an empty array.
args = [Context, Args.slice args.slice (): args];
if (firing) {//when the For loop is executing the method in the list array, firing is true, then fire ()->firewith is invoked, and args is added to the stack only. When the method in the list array finishes executing. is processed according to whether there is a value in the stack. For example: var cb = $. Callbacks (); function A () {Cb.fire ()};funciton B () {};cb.add (a,b); Cb.fire (); Execute a method, and then trigger Fire (), but do not execute the a method again. Because then firing is true, only the stack is added to 1, but wait until the B method is finished before the A method is executed again. The private fire method determines the value of the stack, and if it has a value, it continues to loop through the methods in the list array to invoke execution.
Stack.push (args);
} else {
Fire (args); The method that actually executes the list array is the private method fire.
}
}
return this;
},
Fire:function () {//fire method is actually executing the method in the list
Self.firewith (this, arguments); It calls the Firewith method. Arguments is the argument passed in the fire, which passes the value of the method in the list array.
return this;
},
Fired:function () {
Return!! Fired; Fire returns true as long as the call is once
}
};
return self; Returns the Self object. When calling var cb = $. Callbacks (), CB =self. So calling Cb.add,fire is actually the Self.add and fire method
};