JavaScript堆栈内存与闭包问题图解+文字详解

一、堆栈内存

example1

let a = {},
    b = '0',
    c = 0;
a[b] = '珠峰';
a[c] = '培训';
console.log(a[b]);   //培训

解析:答案是培训。对于引用类型,它的处理方案一定是堆内存来处理的

创建了变量a,给a赋的是一个空对象,对象是引用类型值,得是一个堆内存。

  • 堆内存——用来存储引用类型值的空间
  • 栈内存——存储基本类型值,执行代码的空间在这里插入图片描述

a会指向一个16进制内存地址,右侧框框表示一个堆内存。
b='0', c=0, 都是正常声明变量并赋值,
a['0'] = '珠峰',此时在堆内存地址中放入'0' : 珠峰
又由于堆内存中,属性名是不可以重复的,且数字属性名0与字符串属性名’0’是一样的,于是 a[0] = '培训', 会覆盖掉珠峰,所以a[b]也就是 a[0]的结果是培训

本小问补充提问:JS中对象和数组的区别

example2

let a = {},
    b = Symbol('1'),
    c = Symbol('1');
a[b] = '珠峰';
a[c] = '培训';
console.log(a[b]);   //珠峰

解析:有了example1题目的讲解之后,这题我们主要就是要讨论:

对于a这个引用类型对象而言,b和c是不是同一个属性名

此处的b = Symbol('1')c = Symbol('1')看起来是一样的,
但是要知道:
Symbol是用来创建唯一值的

Symbol('1')  === Symbol('1')   //false

那既然b与c不是同一个属性名,显然a[b]就是'珠峰'

注:对象的属性名不一定只能是字符串。比如可以是Symbol这种,还可以是布尔值、null、undefined。但一定要注意,数字和字符串数字属于一样的。
在这里插入图片描述

可以看到,a是有两个“看起来一样”的属性名和值的。

本小问补充提问:自己实现Symbol

example3

let a = {},
    b = { n:'1' },
    c = { m:'2' };
a[b] = '珠峰';
a[c] = '培训';
console.log(a[b]);   //珠峰

解析:对象引用类型值,都要存成字符串。

let obj = {};
let n = { name: 'xxx'}
obj[n] = 100

dir(obj)
--> [object object] : 100
------------------------------
/*引用类型值存储的时候,都会直接默认调用toString方法,
调用toString方法后就变成"[Object Object]"了*/

({name:'xxx'}).toString()   //"[Object Object]"
({age:12}).toString()   //"[Object Object]"

那么对于这道题中的b = { n:'1' }c = { m:'2' },在a中都会存成 "[Object Object]",那么显然,培训会把珠峰覆盖掉.

a[b]的值是 培训

本小问补充提问:
关于Object.prototype.toString()它的项目中的应用
valueof原始值 与 toString() 的区别

二、闭包作用域

example1

遇到会的题不要马虎,‘4’ 和 4是不一样的

注意!!!alter弹出来的结果,都要toString()转成字符串。 console.log()是正常的,是啥就是啥。

比如 alert({});

在这里插入图片描述

var test = 
    (function(i){
        return function () {
            alert ( i *= 2 );
        }
	})(2);
test(5);   // 字符串'4'

解析: var test = 一个立即执行的自定义函数

浏览器一加载界面就形成了栈内存,而我们每个函数执行,叫,把一个执行上下文压缩到栈内存去执行。每次函数执行,都会形成一个全新的执行上下文(ECstack——Execution Context Stack,执行环境栈)
在这里插入图片描述

图解描述:
  • (function(i){ ... })(2); 这个立即执行函数,会形成一个执行上下文,里面存着形参i
  • 接下来代码开始执行,return了一个function(){ alert(i*=2) }这样的函数,而函数也是引用类型的,而引用类型是由堆内存存储的。
  • 存储这个函数的堆,有一个16进制的内存地址,假设为AAAFFF111。 作为函数,在堆中存储代码(字符串)。对象堆里存的都是键值对,函数也是对象,所以这些东西都是作为键值对存储的。
  • 所以return到的仅仅是, 函数所存储的堆的内存地址AAAFFF111
  • 于是 ,当前function(){ alert(i*=2) }这个函数,它的上级作用域,就是(function(i){ … })(2);`这个函数的执行上下文所在的作用域。这样就产生了作用域链这样的概念。
  • 所以(function(i){ ... })(2);这个自执行函数的返回结果是一个堆内存的地址AAAFFF111。
  • 所以var test = AAAFFF111
  • test(5); 函数执行,又会形成一个执行上下文,压栈执行。
  • test是哪个函数呢,test其实是function(){ alert(i*=2) }这个函数,而这个函数是没有形参的,则不需要形参赋值。
  • 但此时这个蓝色的执行上下文中没有i, 而这个执行上下文又是隶属于AAAFFF111这个函数的,所以去这个函数的上级作用域去找,找到i=2.
  • 所以结果alert(i*=2)是’4’

销毁逻辑:

在这里插入图片描述

  • 这个执行上下文执行完了之后,这里面的东西没有被其他任何人占用,所以它要销毁。
    在这里插入图片描述
  • 这个就不能被回收,因为它还是AAAFFF111那个函数的上级作用域,如果它没了,绿色的堆内存也就没了,那个函数也就不存于堆内存了。但这个函数又被var test占住了,它不能够消失,所以这个执行上下文不能被销毁。

这就是整个的闭包机制, 自执行函数形成的上下文叫闭包的上下文。

闭包是叫:内存不销毁。(不是内存泄漏:内存泄漏是指该销毁的都没销毁,而闭包的不销毁是本就不能销毁)

example2

var a = 0;
	b = 0;
function A(a) {
    A = function(b) {
        alert( a + b++ );
    };
    alert(a++);
}
A(1);    // '1'
A(2);    // '4'

在这里插入图片描述

图解描述:
  • 全局的对象(GO)

    • a = 0
    • b = 0
    • 函数A,存于一个堆内存 记为 A = AAAFFF000
      函数堆内存里存着代码字符串A = function(b) { alert( a + b++);}
  • A(1) ; 执行, 形成一个执行上下文,而函数A里含有另一个function(b) = alert( a + b++ ); }

  • 这个函数又形成了一个堆,内存地址记为BBBFFF000,这个内存里存着alert( a + b++ )这个代码字符串

  • 在A(1) 的执行上下文里,A = BBBFFF000

  • 而A(1) 的执行上下文的上级作用域是全局作用域,里面有了一个A = AAAFFF000,所以全局的A会被覆盖为BBBFFF000

  • 此时A的指向就变了,A指向BBBFFF000这个地址存的function(b) = alert( a + b++ ); }这个函数了。重写了全局方法指向。

  • A(1) 继续执行,alert(a++)先输出结果’1’, 再自增为2,并覆盖掉A(1) ECStack里原有的 a = 1, a目前为2


  • A(2) ; 执行, 形成一个执行上下文,注意!!此时A还是function(b) = alert( a + b++ ); }这个函数。
  • 形参b = 2,
  • alert(a+b++) 输出’4’ 这个结果,b再自增为3
    • ==> a + b = 2+2 = 4
    • ==> b++ , b变为3

  • 而此时,全局的a,b不会受到任何影响,还是0,0; 其实全局的a,b完全没参与这一场闭包哈哈哈
  • 所以,闭包/函数执行的作用除了保存,还有一个作用就是保护
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值