JQuery. extend () method and jQuery. fn. extend () method source code analysis, jquery. fn. extend
The two methods use the same code. One is used to merge attributes and methods for jQuery objects or common objects. The other is an example of jQuery objects. for basic usage, the following are examples:
The html code is as follows:
<!doctype html>
The usage in js is as follows:
Merge two common objects
// Merge the attributes var obj1 = {name: 'Tom ', age: 22} for two common objects; var obj2 = {name: 'jack', height: 180}; console. log ($. extend (obj1, obj2); // Object {name: "Jack", age: 22, height: 180}
Add attributes or methods to jQuery objects
$.extend({hehe:function(){alert('hehe');}});
$.hehe(); //alert('hehe')
This usage is very important. It is also the method for compiling jQuery plug-ins to add instance attributes and METHODS Inside jQuery, as well as the implementation of prototype attributes and methods, the following describes how to use the extend method to extend the attributes of jQuery1.7.1.
jQuery.extend({ noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1,
.....
In this example, only one object parameter is input, so this is used as the object to be merged and modified by default.
Add attributes or methods to jQuery object instances
// Expand and merge the console for jQuery instances. log ($ ('img '). extend ({'title': 'img '}); // [img, img # img. img, prevObject: jQuery. fn. jQuery. init [1], context: document, selector: "img", title: "img", constructor: function…]
Merge only without modifying the objects to be merged
var obj1={name:'Tom',age:22}; var obj2={name:'Jack',height:180}; console.log($.extend(obj1,obj2)); //Object {name: "Jack", age: 22, height: 180} console.log(obj1); //Object {name: "Jack", age: 22, height: 180}
By default, the object to be merged is modified like the returned result. You can use this method if you only want to get a merged object and do not want to destroy any original object.
var obj1={name:'Tom',age:22}; var obj2={name:'Jack',height:180}; var empty={}; console.log($.extend(empty,obj1,obj2)); //Object {name: "Jack", age: 22, height: 180} console.log(obj1); //Object {name: "Tom", age: 22}
Recursive merge or deep copy
var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}}; var obj2={name:'Jack',love:{drink:'water',sport:'football'}}; console.log(($.extend(false,obj1,obj2)).love); //Object {drink: "water", sport: "football"} console.log(($.extend(true,obj1,obj2)).love); //Object {drink: "water", eat: "bread", sport: "football"}
Detailed usage can see reference manual http://www.w3cschool.cc/manual/jquery/
The following describes how to implement the 1.7.1 source code:
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; ...}
First, a set of variables are defined. because the number of parameters is unknown, the parameters passed by the arguments object are directly called.
Variable options: points to a source object.
Variable name: indicates an attribute name of a source object.
Src: the original value of a property of the target object.
Variable copy: indicates the value of an attribute of a source object.
Variable copyIsArray: indicates whether the variable copy is an array.
Variable clone: indicates the modified value of the original value during deep replication.
Target: point to the target object.
Variable I: The starting subscript of the source object.
Variable length: the number of parameters used to modify the variable target.
Variable deep: Indicates whether to perform deep replication. The default value is false.
To better understand the code implementation, the example above is used as an example to demonstrate how the source code is executed.
var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}}; var obj2={name:'Jack',love:{drink:'water',sport:'football'}}; $.extend(true,obj1,obj2)
Source code analysis
// Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; }
Determine whether deep replication is performed. If the first parameter is a Boolean value, the value of the first parameter is given to deep, and the second parameter is used as the target object, if the second parameter does not exist, assign a value to an empty object and change the subscript of the source object to 2, in this example, It is here because the first parameter is true and then deep is changed to true, and target is corrected to the second parameter, that is, obj1, the starting subscript of the source object is 2, which means that the source object is the obj2 object in this example.
// Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; }
Here, the target is further processed. For non-object and function data types, adding custom attributes is invalid. For example, the built-in methods and attributes can be called by the string itself.
// extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; }
If the length attribute is equal to the value of I, it indicates that no target object exists. Normally, the length must be greater than the value of I, in this case, this is used as the target object to reduce the I value by one and realize that the length value is greater than the I value (1 larger than I)
This is the implementation principle of jQuery's method for extending attributes for itself, as long as it does not pass in the target object.
There are two possible cases: $. extend (obj) or $. extend (false/true, obj );
for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } }
This part is the core of this method. Starting from the I lower mark of The arguements object, the circular operation first filters out whether the source object is null or undefined.
The source object may not necessarily be an object or another type of value, such as a string, for example:
console.log($.extend({'name':'tom'},'aa')); //Object {0: "a", 1: "a", name: "tom"}
Isn't it strange? How is it implemented? Next let's take a look.
After filtering, the for loop src is started to save the value of a key of the target object. The copy attribute stores the value of a key of the source object, both of which are the same.
// Prevent never-ending loop if ( target === copy ) { continue; }
If a property value of the source object is the target object, it may cause an endless loop and cause program crash. Therefore, we have made a restriction to skip this loop. For example:
Var o = {}; o. n1 = o; $. extend (true, o, {n2: o}); // throw an exception: // Uncaught RangeError: Maximum call stack size exceeded
However, some normal cases such:
var obj1={a:'a'} var obj2={a:obj1}; console.log($.extend(obj1,obj2)); //Object {a: "a"}
In this case, the value of the source object is equal to the value of the target object, but the result shows that the attribute value of a of obj1 is not modified because the continue is executed. The following comments of the source code are commented out in the execution.
Object {a: Object}
At this time, it is changed normally. I personally feel that this place needs to be improved;
Next, an if statement is used to determine whether or not to perform deep replication first.
target[ name ] = copy;
It is easy to copy the object directly to the target object as long as the copy object has a value. If some of the target objects are modified, the object will be added. In this way, the merge will be realized.
After a for loop, the new target object is returned, so the target object is modified at the end, and the result is the same as the returned result.
// Return the modified object return target;
};
Next, let's talk about how to deal with deep replication.
First, ensure that deep is true, copy has a value and is an object or an array (if it is not an object or an array, it cannot be discussed in depth), and then the score group and object are processed, first, let's look at the array:
if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : [];} else { clone = src && jQuery.isPlainObject(src) ? src : {};}
If the array copyIsArray value is true and then the value is changed to false, the target object may or may not exist for the source object attribute of the current loop, if yes, let's determine whether the array is an array. If the original array remains unchanged, it will be converted into an array. Since the current attribute of the source object is the final target element of the array, it must also be an array. Either an array or an object changes the current attribute of the target object to an object.
// Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy );
Then, the current attribute value (an array or object) of the source object and the current attribute of the transformed target object are recursively merged, and the new array or object returned is assigned to the target object, deep replication is achieved.
However, there is a strange phenomenon, such as the following operations:
console.log($.extend({a:1},'aa')); //Object {0: "a", 1: "a", a: 1}
The original source object is not necessarily the Object e, and the string can be split and merged with the target object. The original for... in loop is the operation string.
var str='aa'; for(var name in str){ console.log(name); console.log(str[name]) }
This is also possible. It will split the string and read it by numerical subscript, but in the source code
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) )
There are restrictions on arrays and objects. Is there no effect in deep replication?
After my tests, deep replication is also possible, because the copy value in the source code turns into an anonymous function.
alert(jQuery.isPlainObject(copy)); //true
I have not figured out why the function is to be solved in the future!