前天刚学完Javascript基础语法,现在对知识点进行梳理,当梳理到数组的常用方法中的splice()方法时,里面提到了浅拷贝的概念。查阅了相关博客:Java 浅拷贝和深拷贝的理解和实现方式,发现要理解浅拷贝和深拷贝的区别,重点还是要理解基本数据类型和引用数据类型的复制过程区别。
splice()方法介绍:该方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。MDN官网资料:Array.slice()
Javascript基本数据类型和引用数据类型的区别和注意点
区别
区别1:基本数据类型的数据值存储在栈内存中,引用数据类型的数据值存储在堆内存中
基本数据类型的数据值存储在栈内存中,变量指向这块栈内存的数据值。通过变量找到这个数据值,只需要一步。
而引用数据类型不仅有数据值,还有一个专门指向这个数据值的地址值(理解为这个数据值的地址管家),并且数据值存储在堆内存中,地址值存储在栈内存中。变量指向地址值,而地址值指向堆内存中的数据值。而通过变量找到这个存储在堆内存中的数据值,需要两步。
区别2:变量复制时,基本数据类型复制的是数据值,引用数据类型复制的是地址值
基本数据类型的变量复制时,复制的是变量的数据值。
而引用数据类型的变量复制时,复制的是变量的指向堆内存中数据值的地址值。两个相同的地址值,指向同一块堆内存中的数据值。
代码用JavaScript编写
基本数据类型变量复制代码:
var num1 = 10;
//将num1的值复制给num2
var num2 = num1;
//复制后,改变num1的值
num1 = 20;
//打印num1和num2,看改变num1的值,是否会影响到num2
console.log(num1, num2);
代码运行结果:
引用数据类型变量复制代码:
var person1 = {
name: "张三",
age: 18,
sayHi: function f() {
console.log(this.name + 'say: "My name is' + this.name + '"');
}
};
//将person1复制给person2
var person2 = person1;
//复制后,改变person1的数据值
person1.name = '李四';
person1.age = 20;
//看改变person1的数据值,对person2的数据值影响
console.log(person2.name,person2.age);
person1.sayHi();
代码运行结果:
基本类型的变量复制过程:首先在栈内存中,开辟一块新的内存空间,然后将变量num1的数据值(10)复制到这块新的栈内存空间中,变量num2指向新的栈内存空间数据值。两个变量指向的数据值互不干扰。
引用类型的变量复制过程:首先在栈内存中,开辟一条新的内存空间,然后将变量person1的地址值!地址值!地址值!(牢记)复制到新的栈内存空间中,使变量person2指向新的栈内存空间中的地址值。两个地址值相同,指向同一块堆内存中的数据值。
注意点
注意点1:
切记:引用数据类型只有new时,才会在堆内存中开辟新的内存空间
引申:new的过程,你真的理解了new的过程了吗?
注意点2:
java中的String是引用数据类型,而JavaScript中的String类型是基本数据类型,在字符串调用字符串的方法时,系统自动创建一个临时字符串对象,当字符串调用完方法后,临时对象被销毁。
String类型比较特殊,其值不变性也要引起注意。
值不变性:改变String类型数据值时,不像其他基本数据类型,在内存中直接修改数据值。而Sting类型修改数据值时,需3步:
1.首先需要重新开辟一块内存空间,并将原来的数据值复制一份到新的内存空间
2.然后在新的内存空间中,对数据值进行修改。
3.将变量指向新的内存空间的数据值。
原来的内存空间没有了引用,系统会过一段时间将其回收。但具体过多久,就不知道了。所以应该避免大量对String类型数据值的修改。
注意点3:
由此引申的知识点:浅拷贝和深拷贝的区别,但只要记住并理解三点,万变不离其宗。1.引用数据类型变量的复制,只是将指向堆内存中的数据值的地址值复制了一份,两个地址值指向同一块堆内存。2.引用数据类型只有new时,才会在堆内存中开辟新的内存空间。3.对String类型数据值的修改,系统会开辟一块内存空间,并将数据值复制给新的内存空间,在新的内存空间中,对复制的数据值进行修改,变量重新指向新内存空间中数据值。具体可参考:Java 浅拷贝和深拷贝的理解和实现方式