js-闭包

闭包

闭包指的是:能够访问另一个函数作用域的变量的函数。清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
    return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
  • JS没有块级作用域,只有全局作用域和局部作用域(函数作用域)。
  • JS中的作用域链,内部的作用域可以访问到外部作用域中的变量和方法,而外部作用域不能访问内部作用域的变量和方法。
  • 当前作用域没有此变量或方法,会向外部作用域寻找变量或方法。

闭包使用场景

1.函数作为返回值

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并不相同。这也体现出了闭包的一个好处:不会造成全局变量污染。

2.函数作为参数

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

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的值。

闭包的实际使用

1.闭包封装变量,收敛权限

// 闭包实际应用中主要是封装变量,收敛权限
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

2.闭包解决递归调用

function  factorial(num) {
   if(num<= 1) {
       return 1;
   } else {
      return num * factorial(num-1)
   }
}
var anotherFactorial = factorial
factorial = null
anotherFactorial(4)   // 报错 ,因为最好是return num* arguments.callee(num-1),arguments.callee指向当前执行函数,但是在严格模式下不能使用该属性也会报错,所以借助闭包来实现


// 使用闭包实现递归
function newFactorial =function f(num){
    if(num<1) {return 1}
    else {
       return num* f(num-1)
    }
}//这样就没有问题了,实际上起作用的是闭包函数f,而不是外面的函数newFactorial

闭包的坑

1.引用的变量可能会发生变化

function outer() {
      var result = [];
      forvar i = 0; i<10; i++{
        result.[i] = function () {
            console.info(i)
        }
     }
     return result
}
//因为每个闭包函数访问变量i是outer执行环境下的变量i,随着循环的结束,i已经变成10了,所以执行每个闭包函数,结果打印10, 10, ..., 10
//解决方法
function outer() {
      var result = [];
      forvar i = 0; i<10; i++{
        result.[i] = function (num) {
             return function() {
                   console.info(num);    // 此时访问的num,是上层函数执行环境的num,数组有10个函数对象,每个对象的执行环境下的number都不一样
             }
        }(i)
     }
     return result
}

2.this指向问题

var object = {
     name: ''object",
     getName: function() {
        return function() {
             console.info(this.name)
        }
    }
}
object.getName()()    // underfined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows

运行object.getName()是一个匿名函数,在全局环境中调用,所以this是指向window

3.内存泄露问题

function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      aler(el.id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
}

// 改成下面
function  showId() {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      aler(id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
    el = null    // 主动释放el
}

参考
1.https://www.cnblogs.com/niehang/p/10552047.html
2.https://www.jianshu.com/p/26c81fde22fb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值