Samgao0312 / Blog

MIT License
1 stars 1 forks source link

再次理解“闭包” #117

Open Samgao0312 opened 2 years ago

Samgao0312 commented 2 years ago

image

1 什么是闭包

闭包也是函数——闭包函数。作为内部函数保持着对外部函数变量的引用。最终总结一下,闭包就是能够引用其他函数内变量(作用域)的函数

上面对闭包的定义都只是闭包的 现象(结果)

  • 闭包就是能够引用其他函数内变量(作用域)的函数;
    • 闭包就是能够将变量保存在内存当中;
    • 闭包内部是可以缓存上级作用域 —— (作用域继承)

由于在JS中,变量的作用域属于函数作用域,在函数执行后作用域就会被清理、内存也随之回收,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数——也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后作用域内的值也不会被销毁。

2 闭包解决了什么?

在本质上,闭包就是将函数内部和函数外部连接起来的一座 桥梁

由于闭包可以缓存上级作用域,那么就使得函数外部打破了 “函数作用域” 的束缚,可以访问函数内部的变量。以我们经常见到的使用的 Ajax 成功回调为例,这里其实就是个闭包,由于上述的特性,回调就拥有了整个上级作用域的访问和操作能力,极大的提高了便利。开发者不用去写钩子函数来操作上级函数作用域内部的变量了。

3 闭包有哪些应用场景?

  1. 一个函数内部返回另一个匿名函数。
  2. 一个Ajax请求的成功回调,
  3. 一个事件绑定的回调方法,
  4. 一个setTimeout的延时回调

简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影。

3.1 return 一个函数

var n = 10;
function fn() {
  var n = 20;
  function f() {
    n++;
    console.log(n);
  }
  return f;
}

var x = fn();
x(); // 20

解释:这里的 return f, f()就是一个闭包,存在上级作用域的引用。

3.2 函数作为参数

var a = '林一一';
function foo() {
  var a = 'foo';
  function fo() {
    console.log(a);
  }
  return fo;
}

function f(p) {
  var a = 'f';
  p();
}
f(foo());   // foo

解析:使用 return fo 返回回来,fo() 就是闭包,f(foo()) 执行的参数就是函数 fo,因为 fo() 中的 a 的上级作用域就是函数foo(),所以输出就是foo

3.3 IIFE(自执行函数)

(function(){
    var name="hello";
    var fn1= function(){
        return name;
    }
    //直接在自执行函数里面调用fn2,将fn1作为参数传入
    fn2(fn1);
})()
function fn2(f){
    //将函数作为参数传入
    console.log(f());//执行函数,并输出
}

3.4 定义私有变量

var Foo = function(){
    var name = 'apple';
    this.getName = function(){
        return name;
    };
    this.setName = function(str){
        name = str;
    };
};
var foo = new Foo();

foo.name;        // undefined, name是私有变量, 无法直接访问
foo.getName();   // 'apple'
foo.setName('banana');    // name changed to 'banana'
function foo(x) {
    var tmp = 3;

    return function (y) {
        alert(x + y + (++tmp));
    }
}

var bar = foo(2); // bar is now a closure.
bar(10); // will also alert 16
bar(10); // will alert 17 because tmp increased 1

这是一个闭包的常见应用,bar是一个foo返回的内部函数,bar使用了变量tmp,所以局部变量tmp不会被销毁,而且每次调用bar都会导致tmp数字加1.

3.5 循环赋值 - 拿到正确的值(老掉牙的问题了😝)

for(var i=0;i<10;i++){
    setTimeout(function(){
        console.log(i)
    },1000)
}
// 输出 10个10
for(var i=0; i<10; i++){
  ((j) => {
    setTimeout(function() {
      console.log(j) 
    }, 1000)
  })(i)
}

原理:声明了10个自执行函数,保存当时的值到内部。

参考