闭包的认识到作用场景

一、什么是闭包

1、概念:

闭包:闭包就是每次调用外层函数时,临时创建的函数作用域对象。内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。  

闭包函数:声明在一个函数中的函数,叫做闭包函数。

因为内层函数作用域链中包含外层函数的作用域对象,且内层函数被引用,导致内层函数不会被释放,同时它又保持着对父级作用域的引用,这个时候就形成了闭包。所以闭包通常是在函数嵌套中形成的。例如以下例子:

function foo (){
  var name = 'snail'
  return function(){
    console.log('my name is '+name)
  }
}
var bar = foo();
bar();

 2、特点:

  让外部访问函数内部变量成为可能;

  局部变量会常驻在内存中;

  可以避免使用全局变量,防止全局变量污染; 

  会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

二、运用场景:

 闭包找到的是  同一地址中 父级函数中 对应变量最终的值  

闭包的实现原理,其实是利用了作用域链的特性,我们都知道作用域链就是在当前执行环境下访问某个变量时,如果不存在就一直向外层寻找,最终寻找到最外层也就是全局作用域,这样就形成了一个链条。 

闭包的形成:

  1. 出现函数嵌套。
  2. 内部函数运用到的外部函数的变量。
  3. 外部函数被调用,内部函数的声明被执行

闭包的目的:

延长变量的声明周期

不会被垃圾回收机制回收,造成内存消耗。可能会导致内存泄露,进而导致内存溢出

这里简单说一下,为什么使用闭包时变量不会被垃圾回收机制销毁呢,这里需要了解一下JS垃圾回收机制

JS规定在一个函数作用域内,程序执行完以后变量就会被销毁,这样可节省内存;

使用闭包时,按照作用域链的特点,闭包(函数)外面的变量不会被销毁,因为函数会一直被调用,所以一直存在,如果闭包使用过多会造成内存销毁。

 1、实现块级作用域:

如果所声明函数没有块级作用域,那么其变量不止是属于其所在的作用域,更是属于其父级作用域,那么输出结果就是最后最终的结果,如下列代码:

function foo(){
  var result = [];
  for(var i = 0;i<10;i++){
    result[i] = function(){
      console.log(i)
    }
  }
  return result;
}
var result = foo();
result[0](); // 10
result[1](); // 10

var 声明的 i 不只是属于当前的每一次循环,甚至不只是属于当前的 for 循环,因为没有块级作用域,变量 i 被提升到了函数 foo 的作用域中。所以每个函数的作用域链中都保存着同一个变量 i,而当我们执行数组中的子函数时,此时 foo 内部的循环已经结束,此时 i = 10,所以每个函数调用都会打印 10

接着对 for 循环内部添加一层即时函数(又叫立即执行函数 IIFE),形成一个新的闭包环境,这样即时函数内部就保存了本次循环的 i,所以再次执行数组中子函数时,结果就像我们期望的那样 result[0]() 打印 0result[1]() 打印 1 ...

function foo(){
  var result = [];
  for(var i = 0;i<10;i++){
    (function(i){
      result[i] = function(){
        console.log(i)
      }
    })(i)
  }
  return result;
}
var result = foo();
result[0](); // 0
result[1](); // 1

当然,ES6 引入了let 声明变量方式,让 JavaScript 拥有了块级作用域,可以更方便的解决这样的一个问题。

 2、函数柯里数

首先说一下什么是函数柯里化?
柯里化是把接收多个参数的函数变成接收单一参数(最初函数的第一个参数)的函数,并且返回接收余下的参数且返回结果的新函数。
翻译成人话就是可以将一个接受多个参数的函数分解成多个接收单个参数的函数的技术,直到接收的参数满足了原来所需的数量后,才执行原函数的逻辑。

例如一个非常经典的面试题 => 实现 add(x)(y)(z) = x+y+z 中就用到了函数柯里化。代码如下:

function add(x){
  return function(y){
    return function(z){
      return x+y+z
    }
  }
}
console.log(add(1)(2)(3)) // 6

 其实就是要生成一个功能,此功能可以拆分成两部分小功能实现,那么这个时候就可以在实现功能一的基础上再加上功能二,形成最终的功能。

3、模块模式:

先看这段代码

function create(){
  var name = 'snail',
  hobby = ['eat','sleep','codeing']
  function say(){
    console.log('my name is '+name+'.')
  }
  function showHobby(){
    console.log(name+' like '+hobby.join(',')+'!')
  }
  return {
    say,
    showHobby
  }
}

var instance = create();
instance.say();       // my name is snail.
instance.showHobby(); // snail like eat,sleep,codeing!

这个模式在 JavaScript 被称为模块。这里我们调用了 create 函数创建了一个模块实例,实例中含有对内部函数的引用,这样保证了内部数据变量是隐藏和私有状态,而返回值则可以看做是模块暴露出的 API。当然这里也可以直接返回方法作为模块的公共 API,·就像 JQuery 中返回的 $

三、缺点:

因为闭包会携带包含它的函数的作用域,所以哪怕外部函数执行完成,因为内部函数保存了外部函数中变量的引用,所以外部函数依然不会被释放,所以过度使用闭包会造成内存占用过多。

 参考引用: 一文彻底搞懂闭包 - 掘金 (juejin.cn)

              闭包,看这一篇就够了——带你看透闭包的本质,百发百中_羊二哥的博客-CSDN博客_闭包 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值