JavaScript 闭包!

1. 闭包:指的是那些引用了另一个函数作用域变量的函数,通常在嵌套函数中出现闭包。

2. 简单理解一下定义和执行一个函数时发生的事情

【假设在全局作用域中定义函数】:首先,在定义一个函数时,会为这个函数创建一个作用域链,它预装载了全局变量对象,之后将这条作用域链保存到这个函数的内部属性 [[ scope ]] 中,当我们执行这个函数时,会创建函数的执行上下文,并复制函数的 [[ scope ]] 来创建它真正的作用域链,接着将函数的活动对象推入作用域链的前端。也就意味着,函数执行时的作用域链上首先是函数本身的活动对象,第二个是全局变量对象,在函数内部调用变量时,先从活动对象上寻找,若未找到,则从全局变量对象上寻找。函数执行完毕后,函数作用域链和活动对象被销毁【函数执行上下文被销毁】,内存中只剩下全局作用域。

【当在函数内部定义函数时】:首先,还是会为这个内部函数创建一个作用域链,预装载了外部函数的作用域链上的变量对象,将这条作用域链保存到函数内部属性 [[ scope ]] 中,当我们执行到这个内部函数时,会创建函数的执行上下文,并复制 [[ scope ]] 创建它的作用域链,将内部函数本身的活动对象推入作用域链前端。

3. 当在一个函数内部返回另一个函数,并且这个内部函数引用了外部函数作用域的变量【闭包】,那么即使外部函数执行完,虽然它的执行上下文的作用域链被销毁了,但它的活动对象不会被销毁,因为其内部函数还未执行,内部函数作用域链上还有着外部函数的活动对象,直到内部函数执行完被销毁,外部函数的活动对象才会被销毁。【执行上下文包含作用域链和活动对象,在内部函数未执行完前,外部函数只是作用域链被销毁,但执行上下文和执行上下文的活动对象没有被销毁】

4. 内部函数无法直接引用外部函数的 this 和 arguments 特殊变量,因此若需要内部函数引用这两个变量需要手动赋值一下。

  window.identity = 'The Window';
  let object = {
    identity: 'My Object',
    getIdentityFunc() {
      return function() {
        return this.identity;
      };
    }
  };
  console.log(object.getIdentityFunc()()); // 'The Window'

改后:

  window.identity = 'The Window';
  let object = {
    identity: 'My Object',
    getIdentityFunc() {
      let that = this;
      return function() {
        return that.identity;
      };
    }
  };
  console.log(object.getIdentityFunc()()); // 'My Object'

5. 内存泄漏问题:闭包很容易引起内存泄漏,当在闭包中访问外部函数获取到的 HTML 元素时,HTML元素永远都无法被销毁,因为内部函数一直引用它,以下为简单示例:

  function assignHandler() {
    let element = document.getElementById('someElement');
    element.onclick = () => console.log(element.id);
  }

易知 element 永远都无法被销毁,修改后:

function assignHandler() { 
 let element = document.getElementById('someElement'); 
 let id = element.id; 
 element.onclick = () => console.log(id);
 element = null; 
}

此时 element 没有再被引用,则其内存会在适当的时侯被释放。

6. 立即调用的函数表达式【立即调用的匿名函数】:可以解决 for 循环中 var 声明的循环迭代变量 i 访问值问题。【在 ES6 中直接使用 let 即可解决此问题,不必使用立即调用的匿名函数,而且还要涉及闭包】

  var arr = [];
  for (var i=0;i<5;i++) {
    arr[i] = function () {
      console.log(i);
    }
  }
  arr[2](); // 5
  var arr1 = [];
  for (var j=0;j<5;j++) {
    arr1[j] = (function (j) {
      return function () {
        console.log(j)
      }
    })(j);
  }
  arr1[2](); // 2

7. 

posted @ 2021-10-18 19:09  TwinkleG  Views(34)  Comments(0)    收藏  举报