一、js数据类型
js数据类型分为基本数据类型和引用数据类型
基本数据类型包括:String Number Boolean Undefined Null Symbol 其特点为:存储在栈内存中
PS:Null与Undefined区别:
当声明的变量还未被初始化时,变量的默认值为Undefined;typeof Undefined 返回值为Undefined
Null 用来表示尚未存在的对象,表示空对象指针,常用来表示函数企图返回一个不存在的对象。typeof null 返回值为object
引用类型包括:array object function 其特点:值为对象,保存在堆内存中。复制时创建指针的副本,指向的是同一对象
注:为什么基础数据类型存在栈中,而引用数据类型存在堆中呢?
- 堆比栈大,栈比对速度快。
- 基础数据类型比较稳定,而且相对来说占用的内存小。
- 引用数据类型大小是动态的,而且是无限的。
- 堆内存是无序存储,可以根据引用直接获取。
在参数传递方式上,值类型是按值传递,引用类型是按共享传递。
var a=10;
var b=a;
b=20;
console.log(a);//10
console.log(b);//20
两者之间赋值没有影响
var a={x:10,y:20};
var b=a;
b.x=30;
console.log(b.x);//30
console.log(a.x);//30
因为a和b都是引用类型,指向了同一个内存地址,即两者引用的是同一个值,因此b修改属性时,a的值随之改动。
function foo(a){ a = a * 10; }
function bar(b){ b.value = 'new'; }
var a = 1;
var b = {value: 'old'};
foo(a);
bar(b);
console.log(a); // 1
console.log(b); // value: new
通过代码执行,会发现: a的值没有发生改变 而b的值发生了改变 这就是因为Number类型的a是按值传递的,而Object类型的b是按共享传递的。
另外:
var obj = { a: 1, b: [1,2,3] }
var a = obj.a
var b = obj.b
a = 2
b.push(4)
console.log(obj, a, b)
结果为:{ a: 1, b: [ 1, 2, 3, 4 ] } 2 [ 1, 2, 3, 4 ]
因为虽然obj本身是个引用类型的变量(对象),但是内部的a和b一个是值类型一个是引用类型,a的赋值不会改变obj.a,但是b的操作却会反映到obj对象上。
二、深拷贝与浅拷贝
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝。
浅拷贝实现方式:
1.简单的赋值语句
2.Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign
是ES6的新函数。Object.assign()Object.assign()
进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
需要注意的是:
Object.assign()可以处理一层的深度拷贝,如下
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
深拷贝实现方式:
- 利用 JSON 对象中的 parse 和 stringify
用JSON.stringify
把对象转成字符串,再用JSON.parse
把字符串转成新的对象。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
这样做是真正的Deep Copy,这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象
,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
也就是说,只有可以转成JSON
格式的对象才可以这样用,像function
没办法转成JSON。
var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 没复制
2.利用递归来实现每一层都重新创建对象并赋值
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
if (typeof initalObj[i] === 'object') {
obj[i] = (initalObj[i].constructor === Array) ? [] : {};
arguments.callee(initalObj[i], obj[i]);
} else {
obj[i] = initalObj[i];
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
3.对象只有一层的话可以使用上面的:Object.assign()函数
Object.assign({}, obj1)
的意思是先建立一个空对象{},接着把obj1
中所有的属性复制过去,所以obj2
会长得跟obj1
一样,这时候再修改obj2.b
也不会影响obj1。
参考链接:https://www.cnblogs.com/Chen-XiaoJun/p/6217373.html
https://www.jb51.net/article/135921.htm