Deep Replication, Shallow Replication and Plug-in Development in JQuery

Keywords: Javascript JQuery Attribute JSON

More than half a year ago, I still stay at the novice village level. There may be many problems in writing articles. The way of thinking and logic are not strict enough. I hope I can point out the problems. Thank you.

=====================================================================

A Brief Analysis of the Contrast between Deep and Shallow Replication and Deep Replication Methods $. extend() Method in JQuery to Extend JQuery Plug-in Way

data type

There are two types of data types, basic data type and reference data type. The data types in ES6 are not discussed. The basic types include string, null, undefined, number, boolean. Reference type: object. It is an object with one or more key-value pairs. The basic data type is stored in the stack memory, while the reference type is stored in the heap memory. The data in the stack memory must be fixed size, but the size of the reference type is not fixed, so it can only exist in the heap memory in white. After assigning the address in the heap memory directly, the reference type data can be accessed.

A little extension:
The parameters in the function are passed by value. It is well illustrated by using basic data types if the parameters of a function are reference types:

    function person(obj){
        obj.name = "Linda";
    }
    var a = new Object();
    person(a);
    console.log(a.name);//Linda

In the above example, it may lead to the misunderstanding that the parameters are passed by reference, in fact, because the incoming parameters and the objects inside the function point to the same object, so it will affect the external values. Modification of objects within a function does not cause external value changes.

    function person(obj){
        obj.name = "Linda";
        obj = new Object();
        obj.name = "Greg";
    }
    var a = new Object();
    person(a);
    console.log(a.name);//Linda

Access mode

Because of the different access modes, when accessing the basic data type, the operation is the value saved by the variable, while the reference type value is the operation of the actual object through the operation of the saved address. Simple type values are assigned to targets directly in shallow replication, but objects cannot be replicated by simple complex values if they are replicated. The result is that when the target reference value changes, it also causes the original object to change.

Shallow duplication

    var person1 = ['Nicholas','Greg',[{
                        name : "linda",
                        age : 21                    
                    },{
                        name : "Nancy",
                        age : 19
                    }]
                ]; 
    var person2 = [];
    // // Reproduction
    for(var i  = 0;i < person1.length;i++){
        person2[i] = person1[i]; 
    }
    person2[2].push('Leo') ;//Change the value of color 1
    console.log(person2);//'Nicholas','Greg',Array(3)
    console.log(person1);//'Nicholas','Greg',Array(3)

This is because the complex value refers to the address of the type once, so the result is that the same array object is operated on.

The way to copy only the first layer attributes is shallow replication. If all data types are basic types, they can be successful. When replicating reference types, they need to be replicated to basic types to ensure that they do not affect each other.

Deep duplication

The basic implementation of the native method is as follows:

    var objA = {
        a : "fc",
        b : "Ig",
        c : {
            d(){
                console.log(1)
            }
        }
    }
    function deepCopy(sub,sup){
        for(var key in sup){
            if(typeof sup[key] === 'object'){
                sub[key] = {};
                //The property value copied to the object is up to the base type value
                deepCopy(sub[key],sup[key]);
            }else{
                sub[key] = sup[key];
            } 
        }
    }
    var objB = {};
    deepCopy(objB,objA);

    objA.c.d = function(){
        console.log(2)
    }
    //Modifying attributes on source objects does not modify duplicated attributes on target objects
    objB.c.d();//1

The principle of deep replication is that if the replicated object refers to the type at the time of replication, the replication is run recursively until it is a simple data type.

The $. extend method

In JQuery's extended method, if one or more objects are passed in, the attributes of the following objects will be copied to the target object. The first parameter can be selected as deep copy (true) or shallow copy, which defaults to shallow copy, and the extended object will be returned.

Merge but not modify object1

$.extend({}, object1, object2);

    var settings = {first:'hello', second: 'world'};
    var options = {first:'hello', second: 'JavaScript',third: 'nodeJs'};
    var results = $.extend({}, settings, options);

Merge and modify the first object

$.extend(obj1,obj2)

    var obj1 = {first: 1, second: {height: 178, weight: 70,length:100}};
    var obj2 = {second: {height:180, weight:65, width: 90}, third: 90};
    $.extend(obj1, obj2);
    
    //The output is: {first: 1, second: {height:180,weight:65, width: 90}, third: 90}.

Deep duplication
$.extend(true,obj1,obj2)

    var obj1 = {first: 1, second: {height: 178, weight: 70}};
    var obj2 = {second: {height:180, weight: 65, width: 90}, third: 90};
    $.extend(true,obj1,obj2);
    console.log(obj1,obj2);
    //The output is: {first: 1, second: Object, third: 90}

Different ways to implement replication

Three known ways, $. extend, lodash, and Underscore all have replication capabilities, but there are some subtle differences.

In Underscore,. clone(), as follows:

    var x = {
        a: 1,
        b: { z: 0 },
        c:[
            2,3,9
        ]
    };

    var y = _.clone(x);
    y.c.push(29);
    console.log(x.c,y.c);//[2, 3, 9, 29][2, 3, 9, 29];
    //_ clone source
    _.clone = function(obj) {
      if (!_.isObject(obj)) return obj;
      return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    };
    //The slice method is used to intercept all the arrays, and the shallow copy method is used to copy the objects and then the key value assignment is done. It can be seen that the function can not achieve deep copy.

(2). $. extend ed replication method
The principle of deep replication under this method is to add parameters to achieve recursive extension, so JQuery can achieve deep replication. Source code (3.2) below:

    jQuery.extend = jQuery.fn.extend = function(){
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[ 0 ] || {},// Common usage jQuery. extend (obj1, obj2), where target is arguments[0]
            i = 1,
            length = arguments.length,
            deep = false;
            /*
            Variable options: Point to a source object.
            Variable name: Represents an attribute name of a source object.
            Variable src: Represents the original value of an attribute of the target object.
            Variable copy: Represents the value of an attribute of a source object.
            Variable copyIsArray: Indicates whether the variable copy is an array.
            Variable clone: Represents the modified value of the original value for deep replication.
            Variable target: Point to the target object and declare with the first parameter value temporarily.
            Variable i: Represents the starting subscript of the source object, declared temporarily with the second parameter value.
            Variable length: Represents the number of parameters used to modify the variable target.
            Variable deep: Indicates whether to perform deep replication, defaulting to false.
            */
            
        if( typeof target === "boolean" ){
        //If the first parameter is true, that is jQuery. extend (true, obj1, obj2);
            deep = target;
            target = arguments[ i ] || {};
            i++;
        }
        
        //Handle case when target is a string or something (possible in deep copy)
        //For example, $. extend ({}, {adress: `LosAngles'})
        if( typeof target !== "object" && !jQuery.isFunction( target ) ){
            target = {};
        }
        // Extend jQuery itself if only one argument is passed
        // Handling this situation jQuery.extend(obj), or jQuery.fn.extend(obj)
        if( i === length ){
            target = this;
            i--;
        }
    
        for( ; i < length; i++ ){
            // Only deal with non-null/undefined values
            if( ( options = arguments[ i ] ) != null ){
                //For example, jQuery.extend(obj1,obj2,obj3,ojb4),options are obj2, obj3...
                for( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
    
                    // Preventing self-reference
                    if( target === copy ) {
                        continue;
                    }
                    // If it is a deep copy, and the copied attribute value itself is an object or an array.
                    if( deep && copy && ( jQuery.isPlainObject( copy ) ||
                        ( copyIsArray = Array.isArray( copy ) ) ) ) {
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && Array.isArray( src ) ? src : [];
                        } else {
                            clone = src && jQuery.isPlainObject( src ) ? src : {};
                        }
                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );
                    //Ordinary objects are defined as objects created by'{}'or'new Object', and as you can see from previous code, this object is not an ordinary object. The code then tests whether the object is a normal object.
                
                    //The previous example goes to this point, assigning directly to the object, so after changing the attributes of the source object, the attributes of the target object will also change.
                    }else if( copy !== undefined ){
                        target[ name ] = copy;
                    }
                }
            }
        }
        return target;
    };
    function Obj(){
        this.a = 1;
    }
    var obj = new Obj();
    var tobeCloned = {o:obj};
    var result  = $.extend(true,{},tobeCloned);
    tobeCloned.o.a = 2;
    console.log(result.o.a)//2
    
    console.log($.isPlainObject(obj));//false

According to extend's source code analysis, in the core code about deep replication, the problem of judging whether the attributes of source objects are "ordinary objects" may lead to the error of deep replication results.

For example, in the above code, it can be seen that judging obj is not a "normal object" will affect the result of deep replication. In this example, when the attributes of the source object are changed, the attributes of the target object are also changed, which obviously does not meet the purpose of deep replication. The same problem does not arise in the following lodash deep replication.

(3) The method of assignment in lodash
The replication methods are. clone() and. cloneDeep(). Where. clone(obj, true) is equivalent to. Clone Deep (obj).

    var arr = new Int16Array(5),
    obj = { a: arr },
    obj2;
    arr[0] = 5;
    arr[1] = 6;
    
    //Int16Array is a typed array. 16-bit two-complement signed integer.
    // 1. jQuery
    obj2 = $.extend(true,{},obj);
    console.log(obj2.a);                            // [5, 6, 0, 0, 0]
    Object.prototype.toString.call(obj2);           // [object Int16Array]
    obj2.a[0] = 100;
    console.log(obj);                               // [100, 6, 0, 0, 0]
    
    //Here jQuery can't handle Int16 Array's deep replication correctly!!!
    // 2. lodash
    obj2 = _.cloneDeep(obj);                       
    console.log(obj2.a);                            // [5, 6, 0, 0, 0]
    Object.prototype.toString.call(arr2);           // [object Int16Array]
    obj2.a[0] = 100;
    console.log(obj);                               // [5, 6, 0, 0, 0]

The above code shows that JQuery cannot copy objects other than JSON objects. Lodash uses a lot of code to implement the new standard object introduced by ES6, and can also make deep replication of Date and RegExp. In terms of the implementation of deep replication, lodash is more efficient and applicable than JQuery. So it can be said that lodash is a class library that embraces the future more.

JQuery plug-in

JQuery plug-ins fall into two main categories: 1, class level 2, and object level.

  • Class methods. Use the $class reference directly, and use it without instantiation. Class methods are set as tool classes in the project.

  • Object level. An instance must be created before the instance method can be invoked through the instance.

$. extend extension

Extending the JQuery class directly is equivalent to static methods. Typical methods are $. ajax. Extending methods:

    $.extend({
        add:function(a,b){
            return a + b;
        },
        divide:function(a,b){
            return a/b;
        }
    })
    //Call mode
    $.add(3,0);
    $.divide(9,3);

$. fn.extend() extension plug-in

This extension method is based on prototype objects, which is commonly used when extending plug-ins. After extension, only JQuery instances can call this method, such as wishing to turn all links on the page red.

    $.fn.myLink = function(){
        this.css({
            "color":"red"
        })
    }
    $("a").myLink();

If you need to operate on each specific element, you can extend this method again.

    $.fn.myLink = function(){
        this.css({
            "color":"red"
        });
        this.each(function(){
            $(this).append($(this).attr("href"));
        })
    }
    $("a").myLink();

Note that DOM elements are traversed inside each ch, so you need to wrap them once before you can use JQuery's method. If we want to be able to customize and set it according to our own needs, we can use the $. extend method to merge objects and use them as parameters, using default values when there are no parameters.
When extend ing, empty objects are used as the first parameter to avoid modifying defaults default attribute values and protect defaults default parameters.

    $.fn.myLink = function(options){
        var defaults = {
            "color" : "red",
            "fontSize" : "18px",
            "lineHeight" : "18px"
        }
        //Empty objects are used here to protect default parameters, avoid reuse errors after modification, pay attention to the default or shallow copy, if options have reference type parameters, it will still make an impression on defaults.
        var setting = $.extend({},defaults,options);
        return this.css({
            "color" : setting.color,
            "fontSize" : setting.fontSize,
            "lineHeight" : setting.lineHeight
        })
    }
    $("a").myLink({
        "color":"#333"
    });

Object-Oriented Plug-in Development

Why do we need an object-oriented plug-in approach? Easy to manage for direct use, and the second does not affect the external namespace.

    ;(function($,window,document,undefined){
        var Beautify = function(ele,opt){
            this.$elements = ele,
            this.defaults = {
                "color" : "red",
                "fontSize" : "18px",
                "textShadow" : "none"
            },
            this.options = $.extend({},this.defaults,opt);
        }
        Beautify.prototype = {
            constructor : Beautify,
            beautiful(){
                return this.$elements.css({
                    "color" : this.options.color,
                    "fontSize" : this.options.fontSize,
                    "textShadow" : this.options.textShadow
                })
            }
        }
        $.fn.myPlug = function(options){
            //this points to the new instance beautify, which has a function beautify(), which returns the specified style.
            var beautify = new Beautify(this,options);
            return beautify.beautiful();
        }
    })(jQuery,window,document,undefined);
    $("").myPlug({
        "fontSize":"30px",
        "textShadow": "3px 2px 3px #ff0000"
    })

Posted by myflashstore on Sat, 06 Jul 2019 12:59:29 -0700