美文网首页
Js的浅拷贝与深拷贝

Js的浅拷贝与深拷贝

作者: 无迹落花 | 来源:发表于2019-01-15 19:14 被阅读54次

堆和栈的区别

堆和栈都是内存中划分出来的用于存储的区域。

深拷贝与浅拷贝的区别就是其在内存中存储的类型不同。

栈(stack)为自动分配的内存空间,它由系统自动释放;而堆(heap)则是动态分配的内存,大小不定也不会自动释放。


ECMAScript的数据类型

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

上面5个是ECMAScript中的5种基本数据类型

基本数据类型存放在栈中

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

基本数据类型数据值不可改变

javascript中的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何方法都无法更改(或“突变”)一个原始值。对数字和布尔值来说显然如此 —— 改变数字的值本身就说不通,而对字符串来说就不那么明显了,因为字符串看起来像由字符组成的数组,我们期望可以通过指定索引来假改字符串中的字符。实际上,javascript 是禁止这样做的。字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。

例如:

//  基本类型的值不可改变

var str='html'
str[0]='c'
console.log(str)    //  html

基本类型的比较是值的比较

基本类型的比较是值的比较,只要它们相等就认为它们是相等的。

//  基本类型的比较是值的比较

var a='html'
var b='html'

console.log(a===b)    //  true

但是注意,比较的时候请使用 === 三个等号,表示严格等于。

比如:

//  js会默认做类型转换

var a=1
var b=true

console.log(a==b)    //  true


引用类型

引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。 每个空间大小不一样,要根据情况进行特定的分配,例如。

var person1 = {name:'apple'};
var person2 = {name:'xiaomi'};
var person3 = {name:'huawei'};
object

引用类型值可变

//  引用类型值可变

var arr=[1,2,3,4,5]
arr[0]=10

console.log(arr)    //  [10, 2, 3, 4, 5]
引用类型的比较是引用的比较

所以每次我们对js中的引用类型进行操作的时候,都是操作其对象的引用。(保存在栈内存中的指针),所以比较两个引用类型,是看其引用是否指向同一个对象。 例如:

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

虽然变量 a 和变量 b 都是表示一个内容为 1,2,3 的数组,但是其在内存中的位置不一样,也就是说变量 a 和变量 b 指向的不是同一个对象,所以他们是不相等的。

它们两个是不相等的
传值与传址

在我们进行赋值操作的时候,基本数据类型的赋值(=)是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中。例如:

var a = 10;
var b = a;

a ++ ;
console.log(a); // 11
console.log(b); // 10
基本数据类型的赋值

所以说,基本类型的赋值的两个变量是两个独立相互不影响的变量。

但是引用类型的赋值是传址。只是改变指针的指向,例如,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响。例如:

//  引用类型的赋值

var a={}
var b=a

a.name='jozo'

console.log(a.name)    //  jozo
console.log(b.name)    //  jozo

引用类型的赋值

浅拷贝

上面的=只是引用,不是浅拷贝。

赋值(=)与浅拷贝的区别

下面是一个例子:

//  赋值(=)与浅拷贝的区别

var obj1={
    name: 'xiaomi',
    age: 13,
    language:['English','Chinese','Germany']
}


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="huawei"
obj3.age=18

obj2.language='Germany'
obj3.language='Greek'


console.log(obj1)
/*
{
    age: 13
    language: (3) ["Greek", "French", "Germany"]
    name: "huawei"
    __proto__: Object
}
*/


console.log(obj2)
/*
    {
        age: 13
        language: Array(3)
        0: "Greek"
        1: "French"
        2: "Germany"
        length: 3
        __proto__: Array(0)
      name: "huawei"
    }
*/


console.log(obj3)
/* 
    {
        age: 18
        language: Array(3)
        0: "Greek"
        1: "French"
        2: "Germany"
        length: 3
        __proto__: Array(0)
      name: "xiaomi"
    }
*/

先定义个一个原始的对象 obj1,然后使用赋值得到第二个对象 obj2,然后通过浅拷贝,将 obj1 里面的属性都赋值到obj3 中。也就是说:

  • obj1:原始数据
  • obj2:赋值操作得到
  • obj3:浅拷贝得到

然后我们改变 obj2name 属性和 obj3name 属性,可以看到,改变赋值得到的对象 obj2 同时也会改变原始值 obj1,而改变浅拷贝得到的的 obj3 则不会改变原始对象 obj1。这就可以说明赋值得到的对象 obj2 只是将指针改变,其引用的仍然是同一个对象,而浅拷贝得到的的 obj3 则是重新创建了新对象。

然而,我们接下来来看一下改变引用类型会是什么情况呢,我又改变了赋值得到的对象 obj2 和浅拷贝得到的 obj3 中的 language 属性的第二个值和第三个值(language 是一个数组,也就是引用类型)。结果见输出,可以看出来,无论是修改赋值得到的对象 obj2 和浅拷贝得到的 obj3 都会改变原始数据。

这是因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。

深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,

浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象

深拷贝与浅拷贝的区别
深拷贝

深拷贝是对对象以及对象的所有子对象进行拷贝。

怎么进行深拷贝呢?

思路就是递归调用刚刚的浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象即可。我们直接来看一下 Zepto 中深拷贝的代码:


    // 内部方法:用户合并一个或多个对象到第一个对象
    // 参数:
    // target 目标对象  对象都合并到target里
    // source 合并对象
    // deep 是否执行深度合并
    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] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
                if (isArray(source[key]) && !isArray(target[key]))
                    target[key] = []
                // 执行递归
                extend(target[key], source[key], deep)
            }
            // 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
            else if (source[key] !== undefined) target[key] = source[key]
    }

    // Copy all but undefined properties from one or more
    // objects to the `target` object.
    $.extend = function(target){
        var deep, args = slice.call(arguments, 1);

        //第一个参数为boolean值时,表示是否深度合并
        if (typeof target == 'boolean') {
            deep = target;
            //target取第二个参数
            target = args.shift()
        }
        // 遍历后面的参数,都合并到target上
        args.forEach(function(arg){ extend(target, arg, deep) })
        return target
    }

在 Zepto 中的 $.extend 方法判断的第一个参数传入的是一个布尔值,判断是否进行深拷贝。

$.extend 方法内部,只有一个形参 target,这个设计你真的很巧妙。因为形参只有一个,所以 target 就是传入的第一个参数的值,并在函数内部设置一个变量 args 来接收去除第一个参数的其余参数,如果该值是一个布尔类型的值的话,说明要启用深拷贝,就将 deep 设置为 true,并将 target 赋值为 args 的第一个值(也就是真正的 target)。如果该值不是一个布尔类型的话,那么传入的第一个值仍为 target 不需要进行处理,只需要遍历使用 extend 方法就可以。

而在 extend 的内部,是拷贝的过程。

相关文章

  • JS中的深拷贝与浅拷贝

    知乎:js中的深拷贝和浅拷贝? 掘金: js 深拷贝 vs 浅拷贝 前言 首先深拷贝与浅拷贝只针对 Object,...

  • js浅拷贝深拷贝

    js浅拷贝,深拷贝的简单实现 基础数据 浅拷贝 深拷贝

  • 浅拷贝和深拷贝

    本文参考:JavaScript中的浅拷贝和深拷贝js 深拷贝 vs 浅拷贝深入剖析 JavaScript 的深复制...

  • js面试题

    1,js的深拷贝与浅拷贝 如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,...

  • JS实现深拷贝、instanceof、判断是否为数组

    JS深拷贝 JS中拷贝对象可以按照拷贝的程度可以分为浅拷贝和深拷贝,有些时候我们需要拷贝之后的对象和拷贝之前的对象...

  • JS中对象的复制

    JS中的对象复制分为两种情况:深拷贝和浅拷贝。深拷贝和浅拷贝的区别在于对数组和对象的拷贝,对它们拷贝时浅拷贝只是拷...

  • 认识js下的浅拷贝与深拷贝

    浅拷贝与深拷贝 首先深拷贝和浅拷贝只针对像 Object, Array 这样的复杂对象的。简单来说,浅拷贝只拷贝一...

  • 深拷贝和浅拷贝

    干货!深拷贝和浅拷贝的区别 深拷贝才是拷贝,浅拷贝就是Retain Copy与Retain的区别 Copy: 根据...

  • iOS深拷贝(MutableCopy)与浅拷贝(Copy)的区别

    深拷贝和浅拷贝的概念 iOS中有深拷贝和浅拷贝的概念,那么何为深拷贝何为浅拷贝呢?浅拷贝:浅拷贝并不拷贝对象本身,...

  • Objective-C中的浅拷贝和深拷贝

    Objective-C中的浅拷贝和深拷贝IOS开发之深拷贝与浅拷贝(mutableCopy与Copy)详解iOS ...

网友评论

      本文标题:Js的浅拷贝与深拷贝

      本文链接:https://www.haomeiwen.com/subject/blqydqtx.html