数据类型:
Javascript中有5种基本数据类型(简单数据类型),分别为:Undefined, Null, Boolean, Number和String; 同时还含有一种复杂数据类型,即对象(虽然js中一切皆为对象)
其中Undefined和Null的区别为:
Undefined: 其实是已申明,但是并未赋值的变量,输出的结构就是undefined
Null:是一个不存在的对象的结果
例如:
var a;
console.log(a); // undefined
console.log(document.getElementById('node')); // 因为没有id为node的节点,故输出为null
数据的存储
* 简单数据类型的存储
简单数据类型的值占据了内存中固定的存储空间,并被保存在栈内存中;
当复制一个简单数据类型的值时,会创建这个值的一个副本,该副本会另行开辟一块栈内存空间,改变该副本的值,不会影响原始变量的值。
var a = 'name';
var b = a; // 将a的值赋给变量b
b = 'age'; // 改变b的值
console.log(a); // name
console.log(b); // age;
注: 简单数据类型的变量,不能给其添加属性
var str = 'javascript';
str.attr ='cat'; // 给基本数据类型添加属性
console.log(str.attr); // undefined
* 复杂数据类型的存储
复杂数据类型即引用数据类型,它的值是对象,保存在堆内存中,包含引用数据类型的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。
所以直接将一个复杂数据类型的变量,赋给另一个变量,实际上只是复制了该变量的指针,因此,两个变量实际上都指向同一个对象
当修改其中一个变量的值时,原始的变量也会跟着改变。
var obj = {
name:'dby',
age: 20
};
var objCopy = obj; // 复制obj给objCopy
objCopy.name = 'mxd'; // 改变复制的变量的属性
console.log(obj); // Object{name:'mxd',age:20}
console.log(objCopy); // Object{name:'mxd',age:20}
深浅拷贝
浅拷贝
其实直接赋值就算是一种浅拷贝,对于基本数据类型的简单赋值,是在栈内存中另行开辟了一片存储空间,对拷贝过来的值进行改变,不会改变原始数据的值。
但是复杂数据类型的值赋值,只是复制了指向该值的指针,当赋值后的值改变时,原始数据的值也会跟随改变,在实际工程中,这是我们不希望的
var obj = {
name:'dby',
age: 20
};
var objCopy = obj; // 复制obj给objCopy
objCopy.name = 'mxd'; // 改变复制的变量的属性
console.log(obj); // Object{name:'mxd',age:20}
console.log(objCopy); // Object{name:'mxd',age:20}
slice实现数组的拷贝
const arr = [1,3,4,5,6];
const arr1 = arr.slice(arr);
arr1[1] = 22; // 修改arr1的某个元素
console.log(arr); // [1,3,4,5,6]
console.log(arr1); // [1,22,4,5,6]
note: 由上面可以看到我们可以使用数组的原生方法进行数组的拷贝,但这个拷贝是不是深拷贝呢?,进一步实验一下:
const arr = [1,3,4,5,[1,3]];
const arr1 = arr.slice(arr);
arr1[4][0] = 222;
console.log(arr); // [1,3,4,5,[222,3]] 原始数组被更改了
console.log(arr1); // [1,3,4,5,[222,3]]
由这个小实验可知,slice只能实现一维数组的深拷贝,不能实现多维数组的深拷贝。
Object.assign()实现对象的拷贝
const obj = {
x: 1,
y: 2
};
const obj1 = Object.assign({}, obj);
console.log(obj1); // { x: 1, y: 2}
obj1.x = 222;
console.log(obj); // { x: 1, y: 2 }
console.log(obj1); // {x: 222, y: 2 } 实现了对象的深拷贝,没有改变原对象
const obj2 = {
x: 1,
y: {
m: 2
}
};
const obj3 = Object.assign({}, obj2);
console.log(obj3); // { x: 1, y: { m: 2 } }
obj3.y.m = 222; // 修改obj3的属性值
console.log(obj2); // { x: 1, y: { m: 222 } } // 将原对象也更改了
console.log(obj3); // { x: 1, y: { m: 222 } }
由此可见,Object.assign()方法也只能实现一维对象的深拷贝,不能实现多维对象的深拷贝
深拷贝
数组
数组的深拷贝,可是借用数组的方法slice()和concat()来解决,有局限性,只能实现一维数组的深拷贝
slice
var arr = ['cat','dog','mouse'];
var arrCopy = arr.slice(0); // 返回arr中0-(length-1)长度的数组,不会改变原数组
arrCopy[0] = 'tigger'; // 改变arrCopy第一项为tigger
console.log(arr); // ['cat','dog','mouse']
console.log(arrCopy); // ['tigger','dog','mouse']
concat
var arr = ['cat','dog','mouse'];
var arrCopy = arr.concat(); // arrCopy = ['cat','dog','mouse'];
arrCopy[0] = 'tigger'; // 改变arrCopy第一项为tigger
console.log(arr); // ['cat','dog','mouse']
console.log(arrCopy); // ['tigger','dog','mouse']
对象
方法一:通过定义一个新的对象,并遍历原对象,将属性逐一拷贝上去。
var obj = {
name:'dby',
age:20
};
var objCopy = new Object();
objCopy.name = obj.name;
objCopy.age = obj.age;
objCopy.name = 'chris';
console.log(obj); // Object{name:'dby',age:20}
console.log(objCopy); // Object{name:'chris',age:20}
方法二:封装一个方法来处理对象的深拷贝
var obj = {
name:'dby',
age:20
};
var deepCopy = function(source){ // 对象深拷贝函数
var result = {};
for(var key in source){
if(typeof source[key] === 'object'){
// 如果属性为object类型,则递归调用深拷贝函数
result[key] = deepCopy(source[key]);
}else{
result[key] = source[key];
}
}
return result;
}
var objCopy = deepCopy(obj);
objCopy.name = 'chris';
console.log(obj); // Object{name:'dby',age:20}
console.log(objCopy); // Object{name:'chris',age:20}