堆栈
一、先来看一道简单的题目
let a={},b='0',c=0;
a[b]="第一";
a[c]="第二";
console.log(a[b]);
输出:“第二”
先理解一下堆和栈的概念:
栈:存放基本数据类型的内存空间
堆:存放引用数据类型的内存空间
在定义a={}时会将a这个引用数据类型存到一个堆中,而a指向的则是这个堆的一个16进制内存地址,而b和c都存在栈中,在进行a[b]="第一"这个操作时相当于给堆空间添加了一个属性名为‘0’的属性,但是由于对象的属性名不能重复,所以a[0]和a[‘0’]指向同一属性,所以会修改了同一个属性
思考一下下面的代码输出什么:
let a={},b={m:2},c={n:12};
a[b]="第一";
a[c]="第二";
console.log(a[b]);
还是"第二",这是因为a[b]和a[c]都等价于a[object:Object];在存储对象到堆中会自动转换为字符串存储
闭包
什么是闭包?简单的来说就是函数里面嵌套一个函数
定义:闭包是指有权访问另一个函数作用域的函数
那么作用就很明显了:
1、可以在函数外部访问到函数内部的变量
2、可以将变量保存在内存中不随函数的销毁而销毁
来看一道简单的小题目:
var test=(function(i){
return function(){
alert(i*=2);
}
})(2);
test(5);
答案是弹出了字符串’4’
这里的函数是一个立即执行函数,在执行test(5)需要先找到上级作用域是否有i变量,而上级作用域就是创建函数的上级作用域也就是在栈中,有i=2,所以在test(5)的空间里的i=2。
不清楚再来看一个题:
var a=0,b=0;
function A(a){
A=function(b){
alert(a+b++);
}
alert(a++);
}
A(1);
A(2);
答案是弹出‘1’和’4’
根据图来看,在执行到function A时会创建一个堆来存储A,再到A(1)会分配一个执行栈来执行这个语句,在ECStack1中,也就是执行函数A里的内容, A=function(b){ alert(a+b++); }相当于重写了函数A的指向地址,重新分配了一个堆地址,但在ECStack1已经完成了a=1和alert(a++)的操作,所以a已经变成了2;再到执行A(2)的时候则执行alert(a+b++),此时a=2,b=传进来的2,所以弹出’4’
深克隆和浅克隆
浅克隆:直接将存储在栈中的值赋值给对应变量,如果是基本数据类型,则直接赋值对应的值,如果是引用类型,则赋值的是地址
深克隆:就是把数据赋值给对应的变量,是拷贝对象各个层级的属性,在内存中开辟一块新内存,将原对象中的所有值全部复制过去,与原对象完全脱离,修改新对象中的属性值不会影响原对象
let obj={
a:100,
b:[10,20,30],
c:{
x:10
},
d:/^\d+$/
};
想一想怎么把obj进行一个浅克隆或者深克隆?
浅克隆:
1.ES6新增的拓展运算符
let obj2={...obj};
2.for循环
let obj2={};
for(let key in obj){
if(!obj.hasOwnProperty(key)) break;
obj2[key]=obj[key];
}
深克隆:
1.JSON.parse(JSON.stringify())
let obj2 = JSON.parse(JSON.stringify(obj))
但是要注意:
1.对象中的方法不能进行copy,会自动忽略
2.对象中的Date时间不能进行copy, JSON.stringify方法会将日期自动转换为常规日期格式
3.正则表达式不能copy , JSON.stringify方法会将正则变为空对象
所以我们需要用递归来解决这些问题
function clone(obj ){
// 过滤特俗情况
if(obj===null) return obj
if(typeof obj !=='object') return obj
if(obj instanceof RegExp){
return new RegExp(obj)
}
if(obj instanceof Date){
return new Date(obj)
}
let newObj = new obj.constructor
for(let key in obj){
if (obj.hasOwnProperty(key)) {
newObj[key] = clone(obj[key] )
}
}
return newObj
}
let obj2 = clone(obj)