js基础--类型与拷贝

一.基本类型与引用类型的区别

        1.常见的五种基本数据类型

Undifined、Null、Boolean、Number和String。这五种基本数据类型可以直接操作保存在变量中的实 际值。

        2.基本类型

var str = 'abc';
var num = 123;
var bool = true;
var und = undefined;
var n = null;
var a = 10;
var b = a;
   b = 20;
console.log(a); // 10

        b获取值是a值的一份拷贝,虽然,两个变量的值是相等,但是两个变量保存两不同的基本数据类型值。 b只是保存了a复制的一个副本。所以,当b的值改变时,a的值依然是10;

 

         3.引用类型

        Javascript引用数据类型是保存在堆内存中的对象,与其它语言不同的是,你不可以直接访问堆内存空间 中的位置和操作堆内存空间。

        只能通过操作对象的在栈内存中的引用地址。所以引用类型的数据,在栈 内存中保存的实际上是对象在堆内存中的引用地址。

        通过这个引用地址可以快速查找到保存在堆内存中 的对象。

var obj1 = {};
var obj2 = obj1;
obj2.name = "obj2";
console.log(obj1.name); // obj2

        声明了一个引用数据类型变量obj1,并把它赋值给了另外一个引用数据类型变量obj2。当我们obj2添加 了一个name属性并赋值"obj2"。

        obj1同样拥有了和obj2一样的name属性。说明这两个引用数据类型变 量指向同一个堆内存对象。obj1赋值给obj2,实际只是把这个堆内存对象在栈内存的引用地址复制了一 份给了obj2,但它们本质上共同指向了同一个堆内存对象

 

二. 深浅拷贝

        1.什么是深拷贝和浅拷贝?

       在面试时经常会碰到面试官问:什么是深拷贝和浅拷贝,请举例说明?如何区分深拷贝与浅拷贝,简单来 说,假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没 变,那就是深拷贝;我们先看两个简单的案例:

var a1 = 1, a2= a1;
console.log(a1) //1
console.log(a2) //1
a2 = 2; //修改 a2
console.log(a1) //1
console.log(a2) //2
var o1 = {x: 1, y: 2}, o2 = o1;
console.log(o1) //{x: 1, y: 2}
console.log(o2) //{x: 1, y: 2}
o2.x = 2; //修改o2.x
console.log(o1) //{x: 2, y: 2}
console.log(o2) //{x: 2, y: 2}

        按照常规思维,o1应该和a1一样,不会因为另外一个值的改变而改变,而这里的o1 却随着o2的改变而 改变了。

        同样是变量,为什么表现不一样呢?为了更好的理解js的深浅拷贝,我们先来理解一些js基本的 概念 —— 目前JavaScript有五种基本数据类型(也就是简单数据类型),它们分别是:Undefined, Null,Boolean,Number和String。

        还含有一种复杂的数据类型(也叫引用类型),就是对象; 引用类型常见的有:Object、Array、Function等

         2.基本类型和引用类型

        ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。

        基本类型值指的是那些保 存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。

        而引用类型值是指那些保存堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保 存对象。

         3. js中的堆内存和栈内存

        计算器语言有一个处理的过程,写的代码会进行解释或编译执行,这个过程是在内存中,内存的使用和 分配,涉及到堆和栈,任何语言都有堆和栈 ,堆和栈都存放在内存中

         3.1 栈内存

        栈:javascript的基本类型就5种:Undefined、Null、Boolean、Number和String,它们都是直接按值存 储在栈中,每种类型的数据占用的内存空间的大小是确定的

 栈由系统自动分配, 例如,声明在函数中一个局部变量var a; 系统自动在栈中为a开辟空间 只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出

         3.2堆内存

        堆:javascript中其他类型的数据被称为引用类型的数据 : 如对象(Object)、数组(Array)、函数(Function) …,

        它们是通过拷贝和new出来的,这样的数据存储于堆中

        其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访 问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要 的数据。

 

        3.3引用关系的拷贝过程

var obj1 = {id:1,name:'obj1'};
obj2 = obj1; //相当于只是复制了一个指针地址,obj1和obj2实际指向的是同一数据对象
所以
obj2.id = 2;
// obj1.id 的结果会同步变化

 

        4.引用类型如何实现深拷贝

          既然已经知道了深拷贝与浅拷贝的来由,那么该如何实现深拷贝?我们先分别看看Array和Object自有方 法是否支持

        4.1 Array:对于数组,我们可以使用slice()和concat() 方法来解决上面问题

                4.1.1  slice

var arr1 = ['a', 'b'], arr2 = arr1.slice();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = 'c'; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]
// 此时,arr2的修改并没有影响到arr1

        4.1.2  concat

var arr1 = ['a', 'b'], arr2 = arr1. concat ();
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["a", "b"]
arr2[0] = 'c'; //修改arr2
console.log(arr1); // ["a", "b"]
console.log(arr2); // ["c", "b"]

         4.1.3 多维数组也可以嘛?

// 我们把arr1改成二维数组再来看看:
var arr1 = ['a', 'b', ['c', 'd']], arr2 = arr1.concat();
arr2[2][1] = 100; 
console.log(arr1); //['a', 'b', ['c', 100]]
console.log(arr2); //['a', 'b', ['c', 100]]
// 咦,arr2又改变了arr1,看来slice()/concat()只能实现一维数组的深拷贝

        4.1.4 借助其他的库:比如jq的extend方法

$.extend( [deep ], target, object1 [, objectN ] )
// deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
// target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
// object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
var a=[0,1,[2,3],4],b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1;  
console.log(a); //[1,1,[1,3],4]
console.log(b); //[0,1,[2,3],4]

         4.2 Object

        4.2.1 利用对象的深拷贝实现原理

        定义一个新的对象,遍历源对象的属性并赋给新对象的属性

var obj = {
   name:'fangfang',
   age: 18
}
var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age
obj.name = 'alice';
console.log(obj); //Object {name: "'alice'", age: 18}
console.log(obj2); //Object {name: "'fangfang'", age: 18}

        4.2.2    JSON.parse(JSON.stringify(obj))

var obj = {
   name: 'fangfang',
   age: 18
}
var obj2 = JSON.parse(JSON.stringify(obj));
obj.name = 'alice';
console.log(obj) // {name: "alice", age: 18}
console.log(obj2) // {name: "fangfang", age: 18}

         4.2.3   JSON.parse(JSON.stringify(obj))深拷贝的坑

        利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;

注意以下几点:

        1、如果obj里面存在时间对象,JSON.parse(JSON.stringify(obj))之后,时间对象变成了字符串。

        2、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象。

        3、如果obj里有函数,undefined,则序列化的结果会把函数, undefined丢失。

        4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null。

        5、JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的, 则使 用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor。

        6、如果对象中存在循环引用的情况也无法正确实现深拷贝。

        5.封装一个deep方法实现对象的深拷贝

var Animal={
    name: "duobi",
    skin: ["red", "green"],
    child: {
        xxx: "xxx"
    },
    say: function(){
        console.log("I am ", this.name, " skin:", this.skin)
    }
}
function deep(dest, obj){
    var o = dest;
    for (var key in obj) {
        if (typeof obj[key] === 'object'){   //判断是不是对象
            o[key] = (obj[key].constructor===Array)?[]:{};
            deep(o[key], obj[key])
        } else {
            o[key] = obj[key]
        }
    }
    return o;
}

 ---end

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值