JS面对对象基础--原型链

词法环境[[environment]]–闭包

闭包定义

一些书籍中对于闭包的定义:

  • 闭包是指有权访问另一个函数作用域中的变量的函数—《JavaScript高级程序设计》
  • 闭包允许函数访问并操作函数外部的变量—《JavaScript忍者秘籍》
  • 闭包使得函数可以继续访问定义时的词法作用域—《你不知道的JavaScript》
  • 所有JavaScript函数都是闭包—《JavaScript权威指南》

MDN

  • 函数和对其周围状态(词法环境)的引用捆绑在一起构成闭包
  • 也就是说,闭包可以让你从内部函数访问外部函数作用域(作用域链)。
  • 在JavaScript中,每当函数被创建,就会在函数生成时生成闭包。

[[Environment]]

[[Environment]]:内见属性

  • 每个函数都有;
  • 存放词法环境的作用;
  • v8引擎中无法读写;

对比之前学过的另一个内见属性[[Prototype]]

  • 每个函数(构造函数)都有;
  • 存放对象原型的作用;
  • 可以使用__proto__读写;

[[Environment]]叫词法环境对象:

  • 整个脚本文件执行前会产生一个
  • 函数实例创建后会产生一个

[[Environment]]属性记录了:当前函数的词法环境对象==>外层函数的词法环境对象==>全局的词法环境对象;这样就形成了作用域链

举例说明:

function fn() {
  const a = 1;
  function fo() {
    console.log('hi')
  }
}
fn()
//此时fn函数的[[Environment]]属性包括:作用域链
//1.当前环境的记录
a:1
fo:()=>console.log('hi')
//2.对外部词法环境的引用(全局词法环境)

闭包的例子:函数与词法环境捆绑

function fn() {
    let a = 1;
    return function fo() {
        console.log(a++)
    }
}
let x = fn()
x();//1
x();//2

//下面两种情况相当于创建了两个实例,
//当前面的函数执行完之后里面的变量和函数就被垃圾回收掉,
//所以不影响后面函数的执行依然打印的是a=1
fn()()//1
fn()()//1

我们分析一下上面函数不考虑词法环境的情况下的执行调用栈:

fn()入栈;变量a入栈;fn()和a一起出栈;x()入栈;(此时出现问题了,x()在执行过程中需要找到a,但是a又没在执行栈中,并不符合我们要的预期)

此时就需要在函数与词法环境捆绑,形成闭包来实现这个问题:函数执行前就形成词法环境写入[[Environment]]属性中:此时fo函数内部词法环境是只有一个console.log函数,外层函数词法环境是a:1,全局词法环境为空。

函数中在执行过程中的内部变量,并不是同函数一样被加入到调用栈中,而是在函数执行前形成词法环境写入[[Environment]]属性中保存。只要有函数就会有闭包,只是v8中有垃圾回收机制,当一个函数执行后不被其他变量所引用,这个函数中的变量就不会被垃圾回收掉。但是当函数执行后有变量引用它时,就这个函数里面的变量就永远不会被垃圾回收掉,需要我们手动操作回收变量。

下面的例子中const a这个变量就是被存放在词法环境对象[[Environment]]中。(下图可以解释为什么要有闭包。)

在这里插入图片描述

在这里插入图片描述

理解垃圾回收问题:

js变量回收规则:

  • 在js中定义的全局变量是不会被销毁的,因为随时都可能会用到这个变量,所以不能被销毁。
  • 具体引用关系的不会被销毁。
    • 如果一个对象不被引用,那么这个对象就会被回收;
    • 如果两个对象互相引用,但是没有被第3个对象所引用,那么这两个互相引用的对象也会被回收。

通过下面两段代码进行对比分析:

function a(){
  var b= 10;
  return function(){
    b++;
    console.log(num);
  }
}
a()(); //11
a()(); //11

分析:
在函数a中返回了一个匿名函数,在这个匿名函数中我们num++了一下,然后在函数外面执行了这个匿名函数函数,现在num是11,然后又执行了一次这个函数,你们应该是12吧,为什么不是呢?

原因:
js为了让没有必要的变量保存在内存中,(我们写的任何变量都是需要内存空间的)在不需要这个变量的时候它就会被销毁。所以每次执行一遍 a()() 则变量b就会被销毁。下次再执行,就会重新声明变量b,所以两次输出都是11。

function a(){
    var b = 0;
    return function(){
        b ++;
        console.log(b);
    }
}
var d = a();
d();//1
d();//2

原因分析:

  • 函数a 被 变量d 引用,更准确的说是 函数a 里面的 匿名函数 被变量d所引用。
  • 因为变量d 保存的是函数a执行完成后的值,而函数a执行完,返回了那个匿名函数,所以变量d等于匿名函数。
    -匿名函数因为使用了 函数a 中的 变量b 并且还被 变量 d所引用,所以就形成了一个闭包。
  • 只要这个变量d不等于null的话,那么那个变量b会一直保存到变量d中不会被销毁。
  • 所以两次执行的结果不一样

垃圾回收问题

1.什么是js的垃圾回收机制?

答:javascript中垃圾收集机制是自动回收的,不用人工操作,这让我们更专注于编辑代码上。 回收垃圾机制是定时执行的,具有周期性。

2.什么时候会有垃圾?

答:在作用域中当整个作用域中的代码执行完后,作用域中的变量和方法都会没用,此时就是被当做垃圾了。比如局部作用域,一个函数执行完,里面的变量就可以被销毁,其占用内存被释放。

3.垃圾回收方式?

答:常用的是标记清除:这样操作:一个变量–>进入环境(被标记,有此标记为不能被清除)–>执行–>离开环境(被标记,这个标记告诉机制能被清除)–>回收机制一段周期后,变量被清除。

4.为什么还要管理内存?

答:分配给web浏览器的内存比桌面的应用的内存少,这是出于安全考虑,为了防止运行js的网页耗尽系统内存导致系统崩溃。所以,开发者发现一旦数据不再用时,就将其值设为null来释放其引用(解除引用),这做法适用于全局变量和全局对象属性。

参考资料:

js闭包变量回收问题

垃圾回收问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值