欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS闭包解析

程序员文章站 2022-04-14 18:26:24
三点注意事项 "JS作用域传送门" JS没有块级作用域,只有全局作用域和局部作用域(函数作用域)。 JS中的作用域链,内部的作用域可以访问到外部作用域中的变量和方法,而外部作用域不能访问内部作用域的变量和方法。 当前作用域没有此变量或方法,会向外部作用域寻找变量或方法。 闭包的两种使用场景 函数作为 ......

三点注意事项

js作用域传送门

  • js没有块级作用域,只有全局作用域和局部作用域(函数作用域)。
  • js中的作用域链,内部的作用域可以访问到外部作用域中的变量和方法,而外部作用域不能访问内部作用域的变量和方法。
  • 当前作用域没有此变量或方法,会向外部作用域寻找变量或方法。

闭包的两种使用场景

函数作为返回值

function f() {
  var a = 100;
  return function () {
    console.log(a);
  }
}
var fn = f();
var a = 200;
fn(); // 输出100

调用fn函数,输出a的值,fn中并没有定义a,所以会向上找a,在f函数的作用域中,有a,值为100。所以就会输出100,并不会输出200。全局作用域中的a和f函数作用域中的a并不相同。这也体现出了闭包的一个好处:不会造成全局变量污染

函数作为参数

function f() {
  var a = 100;
  return function () {
    console.log(a);
  }
}

var fn = f();

function f2(fn) {
  var a = 200;
  fn();
}

f2(fn);// 输出100

调用f2函数,传入fn函数,调用fn函数,输出a为100。

关于for循环和闭包之间的关系

var arr = [];
var i;
for (i = 0; i < 3; i++) {
  arr[i] = function () {
    return i;
  }
}
console.log(arr[0]()); // 输出2

arr[0]()的结果,按照一般的思路来讲,应该是0才对,为什么是2呢?

  1. for循环3次,i的值从0变为2,arr这个数组中也添加了3个函数。
  2. 当调用arr[0]函数时,for循环已经结束了,这时候i的值已经为2了,所以arr[0]函数取到的值为2。

如何解决这个问题?

var arr = [];
var i;
for (i = 0; i < 3; i++) {
  arr[i] = (function (i) {
    return function () {
      return i;
    }
  })(i);
}

console.log(arr[0]()); // 输出0

这里,使用自执行匿名函数构造成一个独立作用域。每一次for循环的时候,都会执行这个匿名函数,并生成 一个匿名函数作用域。
比如第一次循环的时候,i = 0,将i作为参数传入匿名函数中,这样i的值就被保存在匿名函数作用域中。当调用arr[0]函数时,arr[0]就会取到匿名函数中的i的值。

闭包的实际应用

// 闭包实际应用中主要是封装变量,收敛权限
function isfirstload() {
  var _list = [];
  return function (id) {
    if (_list.indexof(id) > 0) {
      return false;
    } else {
      _list.push(id);
      return true;
    }
  }
}

var firstload = isfirstload();
firstload(1); // 返回true
firstload(1); // 返回false
firstload(2); // 返回true

闭包的缺点

目前主流浏览器采用的垃圾收集策略均为标记清除。当变量进入环境(比如,定义一个变量)时,就将这个变量标记为‘进入环境’,当变量离开环境时,就会被标记为‘离开环境’,就会被销毁。在刚才的闭包实际应用中_list变量一直被isfirstload的返回函数引用着,不会随着isfirstload的调用结束而销毁。所以_list变量会一直存在内存中。因此不能滥用闭包,否则就会造成网页性能问题,甚至内存泄漏。当我们不需要这些变量的时候,我们可以把这些变量的值赋值为null