zoukankan      html  css  js  c++  java
  • 从JS的深拷贝与浅拷贝到jq的$.extend()方法

    一、堆内存与栈内存

      堆和栈都是内存中划分出来的用来存储的区域,栈为自动分配的内存空间,它由系统自动释放,堆为动态分配的内存,大小不定也不会自动释放。

    二、js基本数据类型与引用类型的不同

    基本数据类型(boolean,undefined,null,string,number

      1.基本数据类型存放在栈内存中

      是存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。

      2、基本数据类型值不可变

        js中给基本类型赋值或操作基本类型数据时,并没有改变基本类型的原始值,数字和布尔类型的值显然是不可变的,字符串虽然有很多方法去操作,但都是在创造一个新的字符串,它的原始值并没有改变。

      3、基本数据类型的比较是值的比较

      只要他们的值是相等的就认为他们是相等的。

    var a = 1;
    var b = 1;
    console.log(a == b);//true

     这里最好用严格等,否则会进行类型转换;

    var a = 0;
    var b = '0';
    console.log(a == b);//true
    console.log(a === b); //false

    引用类型(数组】、对象、函数)

    1、引用类型存放在堆内存

    2、引用类型值可变

    3、引用类型的比较是引用的比较

    var a = [1,2,3];
    var b = [1,2,3];
    console.log(a === b);//false

    比较两个对象的引用,是看两个引用是否指向同一个对象

    基本数据类型与引用类型赋值比较

    基本数据类型赋值是值的传递,先在栈内存中开辟一段内存,再把值赋值到新的栈中

    var a = 10;
    var b = a;
    
    a ++ ;
    console.log(a); // 11
    console.log(b); // 10

    引用类型的赋值是传址,就是说赋值时是保存在栈中的变量的地址的赋值,这样两个变量指向同一个对象,所以两个变量之间会相互影响;

    var a = {};
    var b = a;
    a.name = "qy";
    console.log(a.name);//qy
    console.log(b.name);//qy
    b.age = 22;
    console.log(a.age);//22
    console.log(b.age);//22
    console.log(a === b);//true

    赋值并不是浅拷贝,赋值和浅拷贝之间的区别

    var obj1 = { //原始数据
      "name" : "zhangsan",
      "age" : "22",
      "language" : [1, [2, 3], [4, 5]] 
    }
    var obj2 = obj1;//赋值得到
    var obj3 = shallowCopy(obj1);//浅拷贝得到
    function shallowCopy(src) {
       var dst = {};
       for (var prop in src) {
           if (src.hasOwnProperty(prop)) {
               dst[prop] = src[prop];
           }
       }
       return dst;
    }
    obj2.name = "lisi";//改变赋值得到的obj2的基本类型属性name,最后结果为原始数据里的属性也随之改变
    obj3.age = "11";//改变浅拷贝得到的obj3的基本类型属性age,结果为原始数据的age属性并没有改变
    obj2.language[1] = ['a', 'b'];//改变赋值得到的obj2的引用类型属性,结果为原始数据及由原始数据浅拷贝得到的obj3的language属性均被改变
    obj3.language[2] = ['c', 'd'];//改变浅拷贝得到的obj3的引用类型属性,结果为原始数据及有原始数据赋值得到的obj2的language属性均被改变
    console.log(obj1);//name:lisi,age:22,language:[1,[a,b],[c,d]]
    console.log(obj2);//name:lisi,age:22,language:[1,[a,b],[c,d]]
    console.log(obj3);//name:zhangsan,age:11,language:[1,[a,b],[c,d]]

    结论:(首先需要明确深拷贝与浅拷贝都是针对引用类型而言的,基本数据类型不涉及到深拷贝与浅拷贝)

    @1 引用类型的赋值是地址的传递,即赋值得到的新数据与原数据指向堆内存中的同一个对象,两者之间仍会互相影响;

    @2 浅拷贝是通过创建一个新的对象(数组,对象),依次去拷贝对象的每一个属性,此时如果对象的属性为一个引用类型,那么拷贝过来的引用类型属性依然是指向与原数据的引用类型属性地址相同的一个对象,因此通过浅拷贝得到的新数据,与原数据的引用类型属性会相互影响;

    @3 那么深拷贝是怎样的呢,深拷贝就是要做到新数据与原数据完全的互不影响(无论是基本类型还是引用类型);

    三、如何进行深拷贝?

    思路:递归调用浅拷贝方法,拷贝每一层次的对象属性;以下为zepto的extend方法

    $.extend = function(target) {
                var deep,
                    args = slice.call(arguments, 1);
                if (typeof target == 'boolean') {
                    deep = target;
                    //target取第二个参数
                    target = args.shift();
                }
                //遍历后面的参数,都合并到target上面
                args.forEach(function(arg){
                    extend(target, arg, deep)
                })
                return target;
            }
            function extend(target, source, deep) {
                for (key in source) {
                    //是深拷贝且为数组或者对象时
                    if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
                        //source[key]是对象,而target[key]不是对象,则target[key]初始化一下,否则递归出错
                        if (isPlainObject(source[key]) && isPlainObject(target[key])) {
                            target[key] = {};
                        }
                        //source[key]是数组,而target[key]不是数组也要初始化一下
                        if (isArray(source[key]) && !isArray(target[key])) {
                            target[key] = [];
                        }
                        //执行递归
                        extend(target[key], source[key], deep);
                    } else if (target[key] !== undefined) {
                        target[key] = source[key];
                    }
    
                }
            }

     四、$.extend()方法(jquery的extend扩展方法)

    1、原型:extend(dest,src1,src2,src3...);

    含义:将src1,src2,src3...合并到dest中,返回值为合并后的dest

    2、dest可为{}

    var newSrc=$.extend({},src1,src2,src3...)//也就是将"{}"作为dest参数。

    含义: 这样就可以将src1,src2,src3...进行合并,然后将合并结果返回给newSrc了;

    例:

    var result=$.extend({},{name:"Tom",age:21},{name:"Jerry",sex:"Boy"})
    //结果为:
    result={name:"Jerry",age:21,sex:"Boy"}

     可见此方法中后面的参数如果和前面的参数存在相同的名称,那么后面的会覆盖前面的参数值。

    3.重载原型1:省略dest参数,则该方法就只能有一个src参数,而且是将该src合并到调用extend方法的对象中去

    例:

    $.extend({
    hello:function(){alert('hello');}
    });
    //将hello方法合并到jquery的全局对象中去

    4、重载原型2extend(boolean,dest,src1,src2,src3...)

    含义:第一个参数boolean代表是否进行深度拷贝,其他参数同上;

    var result = $.extend(true,{}, {name: "a", location:{city: "beijing", pos: 1000}}, {age: 12, location:{city: "hanguo", age: 20}})
    result={name: "a", age: 12, location: {city: "hanguo", pos: 1000, age: 20}};

     var result = $.extend(false, {}, {name: "a", location:{city: "beijing", pos: 1000}}, {age: 12, location:{city: "hanguo", age: 20}});

    result= {name: "a", age: 12, location: {city: "hanguo", age: 20}}}

  • 相关阅读:
    phpdocumentor生成代码注释文档(linux)
    phpstorm扩展
    es教程
    康威定律
    k8s
    tidb调研
    netty 在线教程
    McQueenRPC源码阅读
    DIY一些基于netty的开源框架
    性能测试
  • 原文地址:https://www.cnblogs.com/youyang-2018/p/9141536.html
Copyright © 2011-2022 走看看