js引用类型赋值,深拷贝与浅拷贝

时间:2020-12-11 19:50:30

  JS中引用类型使用等号“=” 赋值,相当于把原来对象的地址拷贝一份给新的对象,这样原来旧的对象与新的对象就指向同一个地址,改变其中一个对象就会影响另外那个对象,也就是所谓的浅拷贝。例如:

var arr = ["One","Two","Three"];

var arrto = arr;
arrto[1] = "test";
document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,test,Three
document.writeln("数组的新值:" + arrto + "<br />");//Export:数组的新值:One,test,Three

  其实很多时候这并不是我们想要的结果,修改新对象时我们希望不要影响原来的对象。

  今天我想说的是jQuery.extend()

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    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;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

  $.extend(target,object1,objectN); 合并object1与objectN至target中,此操作会改变target的结构。

  若不想改变原来对象,可以设置第一个参数为一个空对象{}: $.extend({},object1,objectN);

  $.extend()第一个参数也可以为布尔类型,只是是否是深拷贝。$.extend(true,target,object1,objectN);设为true则表示深拷贝,不设默认为浅拷贝,此处只能设true,不能设false。

 

  The merge performed by $.extend() is not recursive by default; if a property of the first object is itself an object or array, it will be completely overwritten by a property with the same key in the second or subsequent object. The values are not merged. This can be seen in the example below by examining the value of banana. However, by passing true for the first function argument, objects will be recursively merged.

Warning: Passing false for the first argument is not supported.

  Undefined properties are not copied. However, properties inherited from the object's prototype will be copied over. Properties that are an object constructed via new MyCustomObject(args), or built-in JavaScript types such as Date or RegExp, are not re-constructed and will appear as plain Objects in the resulting object or array.

On a deep extend, Object and Array are extended, but object wrappers on primitive types such as String, Boolean, and Number are not. Deep-extending a cyclical data structure will result in an error.

For needs that fall outside of this behavior, write a custom extend method instead, or use a library like lodash.

Examples:

Example: Merge two objects, modifying the first.

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4   <meta charset="utf-8">
 5   <title>jQuery.extend demo</title>
 6   <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
 7 </head>
 8 <body>
 9  
10 <div id="log"></div>
11  
12 <script>
13 var object1 = {
14   apple: 0,
15   banana: { weight: 52, price: 100 },
16   cherry: 97
17 };
18 var object2 = {
19   banana: { price: 200 },
20   durian: 100
21 };
22  
23 // Merge object2 into object1
24 $.extend( object1, object2 );
25  
26 // Assuming JSON.stringify - not available in IE<8
27 $( "#log" ).append( JSON.stringify( object1 ) );
28 </script>
29  
30 </body>
31 </html>

输出为:{"apple":0,"banana":{"price":200},"cherry":97,"durian":100}

object1.banana.price = 300; //改变object1中的banana对象的price属性为300
$( "#log" ).append( JSON.stringify( object1 ) ); //输出为 {"banana":{"price":300},"durian":100},object1的改变会影响object2

 

Example: Merge two objects recursively, modifying the first.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery.extend demo</title>
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
 
<div id="log"></div>
 
<script>
var object1 = {
  apple: 0,
  banana: { weight: 52, price: 100 },
  cherry: 97
};
var object2 = {
  banana: { price: 200 },
  durian: 100
};
 
// Merge object2 into object1, recursively
$.extend( true, object1, object2 );
 
// Assuming JSON.stringify - not available in IE<8
$( "#log" ).append( JSON.stringify( object1 ) );
</script>
 
</body>
</html>

输出为:{"apple":0,"banana":{"weight":52,"price":200},"cherry":97,"durian":100}

 object1.banana.price = 300; //改变object1中的banana对象的price属性为300

   $( "#log" ).append( JSON.stringify( object1 ) ); //输出为 {"banana":{"price":200},"durian":100},object1的改变不会影响object2

 

Example: Merge defaults and options, without modifying the defaults. This is a common plugin development pattern.

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4   <meta charset="utf-8">
 5   <title>jQuery.extend demo</title>
 6   <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
 7 </head>
 8 <body>
 9  
10 <div id="log"></div>
11  
12 <script>
13 var defaults = { validate: false, limit: 5, name: "foo" };
14 var options = { validate: true, name: "bar" };
15  
16 // Merge defaults and options, without modifying defaults
17 var settings = $.extend( {}, defaults, options );
18  
19 // Assuming JSON.stringify - not available in IE<8
20 $( "#log" ).append( "<div><b>defaults -- </b>" + JSON.stringify( defaults ) + "</div>" );
21 $( "#log" ).append( "<div><b>options -- </b>" + JSON.stringify( options ) + "</div>" );
22 $( "#log" ).append( "<div><b>settings -- </b>" + JSON.stringify( settings ) + "</div>" );
23 </script>
24  
25 </body>
26 </html>

输出为:

defaults -- {"validate":false,"limit":5,"name":"foo"}
options -- {"validate":true,"name":"bar"}
settings -- {"validate":true,"limit":5,"name":"bar"}

 

参考资料:jQuery.extend的实现方式