javascript函数式编程07-高阶函数(闭包)

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

紧跟上一篇 ,这一篇主要了解高阶函数(闭包)

什么是闭包?

  • MDN的定义
    • 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
  • 闭包就是能够读取其他函数内部变量的函数
  • 函数可以访问由函数内部定义的变量
function myFunction() {
    var a = 4;
    return a * a;
}
复制代码
  • 函数也可以访问函数外部定义的变量
var a = 4;
function myFunction() {
   return a * a;
}
复制代码

如何理解闭包?得从 变量的作用域及如何从外部读取局部变量说起

  • 变量的作用域
    • 变量的作用域无非就是两种:全局变量和局部变量。
    • Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
   var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999
复制代码
  • 另一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
    var n=999;
  }

  alert(n); // error
复制代码
  • 这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
  function f1(){
    n=999;
  }

  f1();

  alert(n); // 999
复制代码
  • 如何从外部读取局部变量
    • 在函数的内部,再定义一个函数
  function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }
复制代码
  • 函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
function f1(){

    var n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999
复制代码

闭包的用途

  • 可以读取函数内部的变量
function module() {
    var arr = [];
    function add(val) {
        if (typeof val == 'number') {
            arr.push(val);
        }
    }
    function get(index) {
        if (index < arr.length) {
            return arr[index]
        } else {
            return null;
        }
    }
    return {
        add: add,
        get: get
    }
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));//外部是无法直接拿到arr的只能通过get来拿
复制代码
  • 可以让变量的值始终保持在内存中
    • 计数器
      • 我们来实现一个计数器,每调用一次计数器返回值加一:
var counter = 0;
function add() {
   return counter += 1;
}
add();
add();
add();// 计数器现在为 3 
复制代码
  • 全局变量容易被其他代码改变
  • 如果我需要同时用两个计数器,但这种写法只能满足一个使用,另一个还想用的话就要再写个counter2函数,再定义一个counter2的全局变量。
    • 使用闭包解决
function add() {
    var index = 1;
    function counter() {
        return index ++;
    }
    return counter;
}

// test
var addA = add() ;
var addB = add() ;
addA();        // 1
addA();        // 2
addB();        // 1
addB();        // 2
复制代码
  • 延时打印
for (var i = 1; i <= 10; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}
复制代码
  • 这样打印出来的全部都是10,原因是for循环是同步的会在延时1000毫秒的过程中一直执行

等function执行的时候变量i指向的是同一个内存地址,且值已经变成的10

  • 改进,用自执行的函数创建简单的闭包,让每一次for循环的i都在不同的内存地址中且不被销毁
for (let i = 1; i <= 10; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}
复制代码

总结

  • 闭包就是子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。

  • 归根结底,就是利用js得词法(静态)作用域,即作用域链在函数创建的时候就确定了。

  • 子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中,这样就形成了闭包

  • 以上所述是给大家介绍的详解JS的闭包详解及高级技巧,希望对大家有所帮助

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享