详细聊聊闭包在js中充当着什么角色

目录
  • 什么是闭包
    • 闭包就是函数有权访问另一个函数作用域中的变量,此函数和被引用的变量一起构成了闭包
  • 如何观察闭包
  • 闭包的错误认识
    • 1.闭包的产生需要使用 return 暴露出去
    • 2.闭包会导致内存泄漏
  • 闭包导致的问题
  • 闭包的使用场景
    • 1. 单例模式
    • 2. 函数柯里化
    • 3. 与立即执行函数配合使用完成类库的封装
    • 4. 保存私有变量
  • 总结

什么是闭包

开篇明义,概念先行。闭包是什么,为什么说在js中处处充满了闭包。

闭包就是函数有权访问另一个函数作用域中的变量,此函数和被引用的变量一起构成了闭包

文字描述文绉绉的难以理解,看一下代码就能够一目了然了

function test() {
    var a = 1
    var b = function() {
        console.log(a)
    }

    return b
}

在上面的代码例子中,变量 a 处于函数 test 的作用域中,但是函数 b 中可以对变量 a 进行访问。
套用闭包的概念,也就是函数 b 有权访问函数 test 作用域中的变量 a,此时函数 b 与变量 a 就形成了一个闭包。

看了上面的例子,大家是否恍然大悟,这不就是我们在代码中经常写的吗。所以说js中处处充满了闭包。

如何观察闭包

如果一开始我们对于闭包的认识还不是很深刻,我们怎么知道在代码中写出了一个闭包呢?一招教你找出闭包

function test() {
    let a = 1
    return function test1() {
        debugger
        console.log(a)
    }
}

test()()

如上的一段代码,在执行到 debugger 关键字的时候,我们可以打开浏览器的开发者调制工具,此时我们可以从调用栈中看到 Closure 的字样,这就是代表我们写出了一个闭包啦

闭包的错误认识

说完了闭包的概念,再来说说可能大家会对闭包产生的一些错误认识。

1.闭包的产生需要使用 return 暴露出去

首先从闭包的概念上来看就没有说到闭包需要暴露到函数外才叫闭包,而是只要引用了不属于当前函数作用域中的变量就已经产生闭包了。

为什么会有这样的错误认识,是因为我们使用闭包引用了外部作用域中的变量,一般是为了将这个变量或者是这个函数暴露出去,让我们在外部也可以访问到这个变量或者函数,也就是说将闭包暴露到函数外部只是我们的业务需求,而不是闭包的必要条件。

2.闭包会导致内存泄漏

首先我们要知道为什么闭包会导致内存泄漏,是因为我们将闭包暴露到函数外部的时候,闭包内部仍然引用着其外部作用域中的变量,导致外部作用域中的变量无法被垃圾回收机制回收,如果循环引用闭包的话就容易造成内存泄漏的现象。但这是由于我们在使用闭包过程中所引起的,而不是闭包本身的性质所决定的,因此说闭包一定会导致内存泄漏是不严谨的。

(另外在 IE9 之后也对浏览器的垃圾回收机制做了优化,现在已经不容易导致内存泄露了)

闭包导致的问题

作为 js 中八大陷阱之一的循环陷阱,就是由于闭包引起的

for (var i = 0; i < 4; i++) {
    setTimeout(() => {
        console.log(i)
    }, 1000)
}  // 4, 4, 4, 4

执行以上代码,会发现 1s 之后打印了 4个 4,为什么不是打印 0, 1, 2, 3,就是因为 setTimeout 中的回调函数是一个闭包,引用了外部作用域中的 i 变量,但是 i 只有一个,并不会在每个回调中生成新的 i,因此在 1s 后打印的时候访问的是同一个作用域中的 i 变量,因此打印的结果就是 4个 4

如何解决以上问题,有两个方法:

  • 一种是使用 es6 的 let 语法生成块级作用域,这样每个块级作用域中的 i 变量就不是指向同一个 i 变量,不会对彼此产生影响
for (let i = 0; i < 4; i++) {
    setTimeout(() => {
        console.log(i)
    }, 1000)
}  // 0, 1, 2, 3
  • 一种是使用立即执行函数,每个立即执行函数中的变量 i 都是当前外部变量 i 的一个快照
for (let i = 0; i < 4; i++) {
    (function(i) {
        setTimeout(() => {
            console.log(i)
        }, 1000)
    })(i)
}  // 0, 1, 2, 3

闭包的使用场景

说了这么多闭包的性质,甚至闭包还会引发循环陷阱这么重大的问题,那么闭包到底有什么用?面试官问到的时候总不能说 js 处处都是闭包,所以 js 到处都是闭包的使用场景吧。那么我们就来说说闭包的几个经典使用场景

1. 单例模式

var CreateSingleton = (function() {
    var instance = null
    var CreateSingleton = function() {
        if (instance) return instance
        return instance = this
    }
    return CreateSingleton
})()

单例模式是设计模式的一种,目的是为了保证全局中只有一个实例对象,上述代码利用 instance 创建一个闭包。单例模式在组件库保证全局中只有一个弹窗组件尤其好用。

2. 函数柯里化

柯里化是将一个多参数的函数转化成几个单参数的函数嵌套的形式,例如: function test(a, b, c) => function test(a)(b)(c)

function currying(fn, args) {
  var _this = this
  var len = fn.length
  var args = args || []

  return function() {
    var _args = Array.prototype.slice.call(arguments)
    Array.prototype.push.apply(args, _args)

    if(_args.length < len) {
      return currying.call(this, fn, _args)
    }

    return fn.apply(this, _args)
  }
}

3. 与立即执行函数配合使用完成类库的封装

闭包往往配合着立即执行函数来一起使用,能够发挥出强大的效果。因此,往往很多人容易被误导,认为闭包与立即执行函数之间有什么关系,甚至认为立即执行函数就是闭包。但这种认识其实是错误的,立即执行函数与闭包之间没有任何关系。

在 jQuery 盛行的年代,各类规范百花争艳,其中 umd 规范能够兼容多种环境,主要在于其 umd 头部结构的实现

(function( global, factory ) {
    "use strict";
    if ( typeof module === "object" && typeof module.exports === "object" ) {
        module.exports = global.document ?
        factory( global, true ) :
	function( w ) {
            if ( !w.document ) {
		throw new Error( "jQuery requires a window with a document" );
            }
            return factory( w );
	};
    } else {
	factory( global );
    }
})( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {})

以上这种代码的写法使用过 jQuery 开发的人应该非常熟悉吧。

4. 保存私有变量

在实际开发过程中,我们有时候需要对于计算结果进行缓存,或者是保存私有变量而不被外部访问到,就可以使用闭包来实现。

另外,在目前流行的两大前端框架 Vue 和 React 中其实也大量用到了闭包进行相关功能的实现,具体大家可以自己翻翻源码啦~

总结

到此这篇关于闭包在js中充当着什么角色的文章就介绍到这了,更多相关js中的闭包内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解js闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 闭包有三个特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量 使用闭包有一个优点,也是它的缺点,就是可以把局部变量驻留在内存中,可以避免使用全局变量.全局变量在每个模块都可调用,这势必将是灾难性的. 所以

  • javascript深入理解js闭包

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数

  • 一分钟理解js闭包

    什么是闭包? 先看一段代码: function a(){ var n = 0; function inc() { n++; console.log(n); } inc(); inc(); } a(); //控制台输出1,再输出2 简单吧.再来看一段代码: function a(){ var n = 0; this.inc = function () { n++; console.log(n); }; } var c = new a(); c.inc(); //控制台输出1 c.inc(); //

  • 前端开发必须知道的JS之闭包及应用

    在前端开发必须知道的JS之原型和继承一文中说过下面写篇闭包,加之最近越来越发现需要加强我的闭包应用能力,所以此文不能再拖了.本文讲的是函数闭包,不涉及对象闭包(如用with实现).如果你觉得我说的有偏差,欢迎拍砖,欢迎指教.一. 闭包的理论 首先必须了解以下几个概念: 执行环境 每调用一个函数时(执行函数时),系统会为该函数创建一个封闭的局部的运行环境,即该函数的执行环境.函数总是在自己的执行环境中执行,如读写局部变量.函数参数.运行内部逻辑.创建执行环境的过程包含了创建函数的作用域,函数也是在

  • js闭包的9个使用场景

    1.返回值(最常用) //1.返回值 最常用的     function fn(){         var name="hello";         return function(){             return name;         }     }     var fnc = fn();     console.log(fnc())//hello 这个很好理解就是以闭包的形式将 name 返回. 2.函数赋值 var fn2; function fn(){   

  • JavaScript中闭包的写法和作用详解

    1.什么是闭包 闭包是有权访问另一个函数作用域的变量的函数. 简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内.而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数和声明的其他内部函数.当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包. 2.变量的作用域 要理解闭包,首先要理解变量的作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变

  • js闭包的用途详解

    我们来看看闭包的用途.事实上,通过使用闭包,我们可以做很多事情.比如模拟面向对象的代码风格:更优雅,更简洁的表达出代码:在某些方面提升代码的执行效率. 1 匿名自执行函数 我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处, 比如:别的函数可能误用这些变量:造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的). 除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一

  • js闭包实例汇总

    Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 复制代码 代码如下: <script type="text/javascript"> var n=100; function parent(){   alert(n); } parent();//100 </script> 如果在php里 复制代码 代码如下: <?php $n=100; function parent(){   echo $n; }

  • 通俗易懂地解释JS中的闭包

    1. "闭包就是跨作用域访问变量." [示例一] var name = 'wangxi' function user () { // var name = 'wangxi' function getName () { console.log(name) } getName() } user() // wangxi 在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函数的作用域中查找,同样未找到,继续向上回溯,发现在

  • 一不小心就做错的JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(

随机推荐