追根溯源
javascript 数据类型:
-
值类型(基本数据类型):String,Number,Boolean,Null,Undefined,Symbol
-
引用数据类型数据
-
Object,Array,Function,Date...
重点声明:Undefined 和 Null的区别;
-
Undefined 表示变量不含有值;
-
null:可以通过将变量的值设置为null来清空变量;
{ var person, car="moto"; console.log(person);//undefined; console.log(car); // moto; car=null; console.log(car); // null }
堆heap 和 栈stack
什么是堆内存&&什么是栈内存?
- heap:为动态分配的内存空间,它大小不一定,也不会自动释放;
- stack:为自动分配的内存空间,它由系统自动释放;
为什么会有堆内存和栈内存的设计
-
与垃圾回收机制有关,为了使得程序运行时占用的内存最小;
-
栈内存:基本数据类型存储以及回收机制
- 当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法中定义的变量,将会被依次放入这个栈内存中。随着该方法的执行结束,这个方法的内存栈也会被自动销毁。这就是我们通常所说的基本数据类型变量通常都放在栈内存中;
-
堆内存:引用数据类型存储以及回收机制
- 当我们在程序中创建一个对象时,这个对象被保存在运行时数据区,以便反复利用,这个运行时数据区就是堆内存。堆内存中的对象不会随着方法的结束而销毁。即使该方法执行结束,这个对象还可能被另外一个引用变量所引用,比如将一个对象作为参数传递时,此时这个对象依然不会被销毁,只有当一个对象,在没有任何变量引用它时,垃圾回收机制才会在合适的时候进行内存回收;这就是我们通常所说的引用数据类型变量通常都放在堆内存中;
-
值 && 引用
- 值数据类型的变量和值都存放在栈内存中,在变量申明之后,会动态分配一块内存区域,基本数据类型之间的赋值:是直接把栈内存中存的值,传值给变量;(传值)
- 引用类型的变量存在栈内存中,但是值存在堆内存中;实际上栈存放的是,指向堆中的地址,即“引用”。引用类型直接的赋值,实质上是把“引用”赋值给一个变量(传址),所以其指向的堆内存中的值是一样的;
深拷贝和浅拷贝
- 使用场景:
- 深拷贝:在复杂对象里,对象的属性也是对象的时候;
- 浅拷贝:只复制一层对象,当对象的属性是引用类型时,实质上复制的是其引用,当引用指向的值发生变化的时候,原对象属性值也跟着变化;
浅拷贝
将原对象/原数组的引用,直接赋给新对象/新数组,新对象/数组只是原对象的一个引用, Just show the code :
举例1:
let obj={a:1,arr:[2,3]};
let shallowObj=shallowCopy(obj);
function shallowCopy(srcObj){
var dest={};
for(let prop in srcObj){
console.log(prop);
if(srcObj.hasOwnProperty(prop)){
dest[prop]=srcObj[prop]
}
}// end of loop
return dest;
}
// 1.举例: 当一个对象属性的引用值改变时,会导致另一个也改变;
shallowObj.arr[1]=5;
console.log(obj.arr[1]); // 5
// 2.举例:console:
let obj2=obj;
obj2
{a: 1, arr: Array(2)}
obj2.arr
(2) [2, 3]
obj2.arr[1]=4
4
obj2.arr
(2) [2, 4]
obj.arr
(2) [2, 4]
深拷贝:
是指,建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用” 我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
1.只对第一层级深拷贝
1.直接遍历:
var array = [1, 2, 3, 4];
function copy (array) {
let newArray = []
for(let item of array) {
newArray.push(item);
}
return newArray;
}
var copyArray = copy(array);
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
console.log(copyArray); // [100, 2, 3, 4]
2.slice(); arrObj.slice(start,end);
slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组
var array = [1, 2, 3, 4];
var copyArray = array.slice();
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
console.log(copyArray); // [100, 2, 3, 4]
3.concat();arrObj.concat(arr1,arr2...);
var array = [1, 2, 3, 4];
var copyArray = array.concat();
copyArray[0] = 100;
console.log(array); // [1, 2, 3, 4]
因为我们上面调用concat的时候没有带上参数,所以var copyArray = array.concat();实际上相当于var copyArray = array.concat([]);
也即把返回数组和一个空数组合并后返回
4.ES6的Object.assign();
Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target Object.assign(target, source1, source2);
var obj = {
name: '彭湖湾',
job: '学生'
}
var copyObj = Object.assign({}, obj);
copyObj.name = '我才不叫彭湖湾呢! 哼 (。・`ω´・)';
console.log(obj); // {name: "彭湖湾", job: "学生"}
console.log(copyObj); // {name: "我才不叫彭湖湾呢! 哼 (。・`ω´・)", job: "学生"}
所以copyObj=Object.asssign({},obj);是将代码中obj的一级属性,拷贝到{}中,然后将其返回给copyObj;
5 ES6扩展运算符:
扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中
var copyObj=[...arr]
function copy(obj){
if(typeof obj !==="object"){
return ;
}
let newObj= obj.constructor===Array?[]:{};
if(newObj instanceof Array){
newObj=[...obj];
return newObj;
}else if(newObj instanceof Object){
newObj={...obj};
return newObj;
}
}
多次级深拷贝;
1.递归方式实现深拷贝
function deepCopy(obj){
if(obj instanceof Array){
let n=[];
for(let i=0;i<obj.length;i++){
n[i]=deepCopy(obj[i]);
}// end of for
return n;
}// end of if ;
if(obj instanceof Object){
let n={};
for(let i in obj){
n[i]=deepCopy(obj[i]);
}// end of for
return n;
} else { //// end of if
return obj;
}
}
2.JSON.parse(JSON.stringify(strObj));
var arrObj=[
{num:1},
{num:2},
{num:3}
];
var copyArray=JSON.parse(JSON.stringify(arrObj));
copyArray[2].num=4;
console.log(arrObj); // [{num:1},{num:2},{num:3}];
console.log(copyArray); // [{num:1},{num:2{num:4}];
3.递归2:
function copy(obj){
let newObj=obj.constructor===Array?[]:{};
if(type of obj !="object"){
return;
}
for(let i in obj){
newObj[i]= typeof obj[i]==="object"?copy(obj[i]):obj[i]
}
return newObj;
}