闭包用于理解案例

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建

案例一:

function init() {
  var name = "Mozilla"; 
  function displayName() {
    alert(name); 
  }
  displayName();
}
init();
function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

这两段代码的区别在于闭包的使用方式和生命周期。

在第一段代码中:

  • init() 函数内部定义了一个局部变量 name,并且在函数内部声明了另一个函数 displayName()
  • init() 函数中,直接调用了 displayName() 函数,导致 displayName() 函数只执行了一次,并且在 init() 函数执行完毕后就被释放了。

而在第二段代码中:

  • makeFunc() 函数内部定义了一个局部变量 name,并且在函数内部声明了另一个函数 displayName()
  • makeFunc() 函数返回了 displayName() 函数的引用,并赋值给了变量 myFunc
  • 在调用 myFunc() 时,实际上是调用了 displayName() 函数。
  • displayName() 函数内部,它引用了外部函数 makeFunc() 中的变量 name
  • 因为 makeFunc() 返回的是 displayName() 函数本身,所以 displayName() 函数被保存下来,并且可以在后续的调用中继续访问和操作外部环境中的变量 name。这符合闭包的定义,因此 displayName() 函数是一个闭包。

因此,第二段代码中的函数可以被保存并多次调用。

 

 案例二:

function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

第一段代码:

运行这段代码后,你会发现它没有达到想要的效果。无论焦点在哪个 input 上,显示的都是关于年龄的信息。

 onfocus 的是闭包。这些闭包是由他们的函数定义和在 setupHelp 作用域中捕获的环境所组成的。这三个闭包在循环中被创建,但他们共享了同一个词法作用域,在这个作用域中存在一个变量 item。这是因为变量 item 使用 var 进行声明,由于变量提升,所以具有函数作用域。当 onfocus 的回调执行时,item.help 的值被决定。由于循环在事件触发之前早已执行完毕,变量对象 item(被三个闭包所共享)已经指向了 helpText 的最后一项。 

第二段代码:

这段代码可以如我们所期望的那样工作。所有的回调不再共享同一个环境, makeHelpCallback 函数为每一个回调创建一个新的词法环境。在这些环境中,help 指向 helpText 数组中对应的字符串。

案例三

 function peoper1(){
    let name = 'xx'
    function print(){
        console.log(name);
    }
    print()
  }
  function print(name){
    console.log(name);
  }
  function peoper2(){
    let name = 'xx'
    print(name)
  }

在内存占用和性能方面,peoper1()peoper2() 的差异主要取决于函数调用的方式和对内存的使用。

  • 对于 peoper1()

    • peoper1() 内部定义了一个函数 print(),这个函数是一个闭包,可以访问外部函数中的局部变量 name
    • 在执行 peoper1() 时,会额外占用一些内存来存储闭包函数 print()
    • 由于闭包函数需要访问外部变量,可能会在一定程度上影响性能,因为涉及到作用域链的查找和变量访问。
  • 对于 peoper2()

    • peoper2() 直接调用全局范围内定义的 print() 函数,将局部变量 name 作为参数传递给该函数。
    • 在执行 peoper2() 时,不需要额外存储闭包函数,因此内存占用可能相对较少。
    • 由于直接传递参数给函数,可能在一定程度上提高性能,避免了闭包函数的作用域链查找过程。

总体而言,在内存占用方面,peoper2() 可能略优于 peoper1(),因为它没有存储闭包函数。在性能方面,由于 peoper2() 直接传递参数给函数,可能稍微优于 peoper1(),但这种差异通常是微不足道的,除非代码运行在高频率或大规模数据处理的情况下。在实际应用中,通常不会因为这种微小的差异而选择一个函数设计而取舍。

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值