js中的函数嵌套和闭包详情

目录
  • 一、作用域
  • 二、函数的返回值
  • 三、函数嵌套
  • 四、闭包
  • 五、闭包的实际应用
    • 1、隐藏内部变量名称和函数执行暂停
    • 2、setTimeout函数传递参数
    • 3、回调
    • 4、函数防抖
  • 六、使用类实现类似闭包中隐藏内部变量功能

前言:

今天就先和大家一起聊一聊我理解的闭包。在聊这个问题之前,先了解一下变量的定义域。
在js中,变量定义域有全局作用域和局部作用域之说。es6中新出现的变量声明关键字,就是为了解决部分变量作用域混乱引入的。全局作用域在这就不谈了。主要说说函数的作用域。

一、作用域

简单一点说,函数的作用域,就是函数的花括号内部,先看两个例子,或许能对这个概念理解的更好一点

function f1(){
  let n = 999
  console.log(n)
}
f1() // 999

function f2(){
  let n = 999
}
alert(n); // 报错

二、函数的返回值

要说闭包之前,我得先说一下函数返回值。关于函数的返回值,小编也是年初才有了一个更深层次的理解。没有返回值的函数,执行之后会返回undefined,有返回值的函数,执行之后就变成了对应的返回值。就像这样

// 没有返回值的函数
function f1(){
  alert(666)
}
console.log(f1()) // 出现弹窗之后,在控制台输出undefind

// 存在返回值
function f2(){
  alert(666)
  return 'over'
}
console.log(f2()) // 出现弹窗之后,在控制台输出over。当然,可以返回字符串,也可以返回Bealon,还可以返回函数。

三、函数嵌套

在《重构——改善既有代码的设计》中,提出了js语法是允许函数内部嵌套函数的,但并不是所有的编程语言都可以的,所谓代码嵌套,就是在函数内部又有函数声明,

就像这样:

function outer(){
  let name = 'lilei'
  function inner(){
    console.log(name)
  }
}

四、闭包

前面说明了在js中的局部变量作用域的问题,在实际项目中,就是需要在函数外部,访问函数内部的变量,这个时候,按照局部变量作用域的问题。似乎是不可能的,闭包的出现,解决了这个问题。

function outer(){
  let name = 'lilei'
  function inner(){
    return name
  }
  return inner
}

上面是一个典型的闭包函数,在使用这个闭包函数的时候,我们可以这样:

let g = outer()
console.log(g()) // lilei

至此,已经解决了在全局访问函数内的局部变量。但是小编在回家的路上在想,为了实现这个功能,是不是不用这个麻烦,我通过这样的函数,也是可以满足需求的。

function outer(){
  let name = 'lilei'
  return name
}

console.log(outer()) // lilei

确实上面的代码和通过闭包最终在控制台输出的内容是一样的,那为什么还要引入闭包呢?小编也是想了接近一周才明白的,这就好比变量->函数->类,每层往上都是逐步提升的过程,通过函数可以实现更多的逻辑,比如对数据进行处理,仅仅靠变量是不能实现的。

五、闭包的实际应用

上面小编介绍了闭包,那么在实际项目中有什么应用呢?先看下面代码:

1、隐藏内部变量名称和函数执行暂停

function outer() {
    let name = 1
    function inner() {
        return name ++
    }
    return inner
}
let g = outer()
console.log(g()) // 2
console.log(g()) // 3
console.log(g()) // 4
console.log(g()) // 5

2、setTimeout函数传递参数

默认的setTimeout是这样的:

小编也曾经这样试验过

function f1(p) {
    console.log(p)
}
setTimeout(f1(666),3000) // 并没有延时,直接输出666

要想通过延时来实现对函数传递参数,这时候,闭包的作用就显现出来了。

function f1(a) {
    function f2() {
        console.log(a);
    }
    return f2;
}
var fun = f1(1);
setTimeout(fun,1000); // 一秒之后打印出1

3、回调

  定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调(事件触发时调用的函数)绑定到事件。就像下面这块代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>
    <a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  id="size-12">12</a>
    <a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  id="size-20">20</a>
    <a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  id="size-30">30</a>

    <script type="text/javascript">
        function changeSize(size){
            return function(){
                document.body.style.fontSize = size + 'px';
            };
        }

        var size12 = changeSize(12);
        var size14 = changeSize(20);
        var size16 = changeSize(30);

        document.getElementById('size-12').onclick = size12;
        document.getElementById('size-20').onclick = size14;
        document.getElementById('size-30').onclick = size16;
</script>
</body>
</html>

4、函数防抖

  在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

   实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。就像下面这样:

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
    let timer = null    //借助闭包
    return function() {
        if(timer){
            clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
            timer = setTimeOut(fn,delay)
        }else{
            timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
        }
    }
}

六、使用类实现类似闭包中隐藏内部变量功能

上面是一个关于闭包的实际应用,小编在晚上睡不着觉的时候,想起同样的需求,是不是也可以通过类来实现呢?最后经过一顿折腾,答案是肯定的,就像这样:

class Adder{
    constructor(c){
        this._c = c
    }
    increace(){
        this._c ++
    }
    decreace(){
        this._c --
    }
    get finalNum(){
        return this._c
    }
}
let c = new Adder(1)
c.increace()
console.log(c.finalNum) // 2
c.increace()
console.log(c.finalNum) // 3
c.increace()
console.log(c.finalNum) // 4
c.decreace()
console.log(c.finalNum) // 3

参考文章:

https://www.cnblogs.com/gg-qq...

https://www.cnblogs.com/pikac...

https://developer.mozilla.org...

(0)

相关推荐

  • Javascript作用域与闭包详情

    目录 1.作用域 2.作用域链 3.词法作用域 5.闭包的应用 6.闭包的缺陷 7.高频闭包面试题 1.作用域 简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限 在ES5中,一般只有两种作用域类型: 全局作用域:全局作用域作为程序的最外层作用域,一直存在 函数作用域:函数作用域只有在函数被定义时才会被创建,包含在父级函数作用域或全局作用域中 说完概念,我们来看下面这段代码: var a = 100 function test(){ var b = a * 2 var

  • 详解JavaScript闭包问题

    闭包是纯函数式编程语言的传统特性之一.通过将闭包视为核心语言构件的组成部分,JavaScript语言展示了其与函数式编程语言的紧密联系.由于能够简化复杂的操作,闭包在主流JavaScript库以及高水平产品代码中日益流行起来. 一.变量的作用域 在介绍闭包之前,我们先理解JavaScript的变量作用域.变量的作用域分为两种:全局变量和局部变量. 1.全局变量 var n = 999; //全局变量 function f1() { a = 100; //在这里a也是全局变量 alert(n);

  • 一文了解JavaScript闭包函数

    目录 变量作用域 闭包的概念 闭包的用途 闭包的缺点 最后总结一下闭包的好处与坏处 总结 变量作用域 要理解JavaScript闭包,就要先理解JavaScript的变量作用域. 变量的作用域有两种:全局的和局部的(全局变量和局部变量) JavaScript中,在函数内部可以直接读取到全局变量. var n=10 function fn(){ alert(n) } fn() //10 而在函数外部无法读取到函数内部的变量. function fn(){ var n=10; } fn() aler

  • JavaScript中的函数嵌套使用

    在JavaScript1.2之前,函数定义是只允许在顶层全局代码,但1.2的JavaScript可以嵌套函数定义其他函数中也是可以的. 仍然存在的函数定义可以循环或条件之内不会出现限制.在函数定义这些限制只适用于函数声明与函数语句. 函数文本(在JavaScript1.2引入的另一个功能)可能出现在任何JavaScript表达式,这意味着它们可以出现在if else语句内. 示例: 下面就是我们两个嵌套函数的例子.这可能会有点混乱,但它的工作原理完全正常: <script type="te

  • JS难点同步异步和作用域与闭包及原型和原型链详解

    目录 JS三座大山 同步异步 同步异步区别 作用域.闭包 函数作用域链 块作用域 闭包 闭包解决用var导致下标错误的问题 投票机 闭包两个面试题 原型.原型链 原型对象 原型链 完整原型链图 JS三座大山 同步异步 前端中只有两个操作是异步的: 定时器异步执行; ajax异步请求 编译器解析+执行代码原理: 1.编译器从上往下逐一解析代码 2.判断代码是同步还是异步 同步:立即执行 异步:不执行.放入事件队列池 3.等所有同步执行完毕开始执行异步 同步异步区别 api : 异步有回调,同步没有

  • JavaScript 函数用法详解【函数定义、参数、绑定、作用域、闭包等】

    本文实例讲述了JavaScript 函数用法.分享给大家供大家参考,具体如下: 初始函数 Function类型,即函数的类型. 典型的JavaScript函数定义: function 函数名称(参数表){ //函数执行部分 return ; } //注意:参数列表直接写形参名即可 return语句:return返回函数的返回值并结束函数运行 函数也可以看做数据来进行传递 参数列表相当于函数入口,return 语句相当于函数出口 函数可以作为参数来传递. function test ( a ) {

  • JavaScript中let避免闭包造成问题

    关于 let 避免闭包带来的问题 利用面向对象思想完成买家信息删除功能,每一条信息包含: 姓名 电话 电话号码 省份 实现以下要求: 不能借用任何第三方库,需要使用原生代码实现. 结合给出的基本代码结构,在下方2处code here补充代码,完成买家信息的删除功能,注意此页面要在手机上清晰显示. js代码可以任意调整,例如和使用es6代码完成. <!DOCTYPE html> <html> <head> <meta charset="utf-8"

  • js中的函数嵌套和闭包详情

    目录 一.作用域 二.函数的返回值 三.函数嵌套 四.闭包 五.闭包的实际应用 1.隐藏内部变量名称和函数执行暂停 2.setTimeout函数传递参数 3.回调 4.函数防抖 六.使用类实现类似闭包中隐藏内部变量功能 前言: 今天就先和大家一起聊一聊我理解的闭包.在聊这个问题之前,先了解一下变量的定义域. 在js中,变量定义域有全局作用域和局部作用域之说.es6中新出现的变量声明关键字,就是为了解决部分变量作用域混乱引入的.全局作用域在这就不谈了.主要说说函数的作用域. 一.作用域 简单一点说

  • js中匿名函数的创建与调用方法分析

    本文实例分析了js中匿名函数的创建与调用方法.分享给大家供大家参考.具体实现方法如下: 匿名函数就是没有名字的函数了,也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数.最经常用作回调函数(callback)参数的值,很多新手朋友对于匿名函数不了解.这里就来分析一下. function 函数名(参数列表){函数体;} 如果是创建匿名函数,那就应该是: function(){函数体;} 因为是匿名函数,所以一般也不会有参数传给他. 为什么要创建匿名函数呢?在什么情况下会使用到匿

  • 浅析JS中对函数function的理解(基础篇)

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定义方式(见下文)之外,还有一种定义的方式能更直观的体现出这个概念: var sum = new Function("num1", "num2", "return num1 + num2"); //不推荐 Function的构造函数可以接收任意数量的参

  • 浅谈js中同名函数和同名变量的执行问题

    经测试未写成闭包形式的在同一个文件中或者不同的 js 文件中定义的同名函数,调用时会执行后面一个定义的函数.即使这样写也会执行后面一个即会弹出2: <script type="text/javascript"> function t(){ alert(1); } t(); function t(){ alert(2); } </script> 另外,定义的变量与css样式也是以后面的为准. 但是对于函数,经测试这样写却会执行前面的函数直接量即弹出1,暂时不知道是

  • JS中箭头函数与this的写法和理解

    前言 JavaScript在ES6语法中新增了箭头函数,相较于传统函数,箭头函数不仅更加简洁,而且在this方面进行了改进.this作为JavaScript中比较诡异的存在,许多文章对于this的解释也不尽相同,本篇文章试图厘清JS中函数与this的关系. 一.JS中函数的写法 1.常规函数的写法 在ES6语法之前,JS中的函数由function关键字.params参数和被花括号包裹的函数体组成.为了与后面说到的箭头函数相区别,我们先把这样的函数叫做常规函数,常规函数既可以用声明式写法也可以用赋

  • 对js中回调函数的一些看法

    最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做native App  和Web App是主流,也就是说现在各种基于浏览器的web app框架也会越来越火爆了,做js的也越来越有前途.我也决定从后端开发渐渐向前端开发和手机端开发靠拢,废话不说了,我们来切入正题"js的回调函数"相关的东西. 说起回调函数,好多人虽然知道意思,但是还是一知半解.至于怎么用,还是有点糊涂.网上的一些

  • 关于原生js中bind函数的简单实现

    今天继续研究了bind函数的实现,也知道了shim和polyfill的说法,现在总结一下, if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new Typ

  • JS中encodeURIComponent函数用php解码的代码

    JS中encodeURIComponent函数给中文编码后,如何用php解码?? 前提:编码前的中文可能是gbk,gb2312,utf-8等. 复制代码 代码如下: urldecode() iconv() 在JS中使用了encodeURIComponent对中文进行编码在PHP中使用iconv('UTF-8','gb2312',$q);就可以得到你需要的字串了,其中gb2312根据你实际应用来定如还不明白为什么看下面的文章 URL编码转换,escape() encodeURI() encodeU

  • js中eval()函数和trim()去掉字符串左右空格应用

    对于js中eval()函数的理解和写一个函数trim()去掉字符串左右空格. trim()是参照了jquery的源码,你可以放心使用. 对于js中eval()函数的理解是本人心得不一定正确. 复制代码 代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <he

  • 浅谈js中test()函数在正则中的使用

    test() 方法用于检测一个字符串是否匹配某个模式. 返回一个 Boolean 值,它指出在被查找的字符串中是否匹配给出的正则表达式. regexp.test(str) 参数 regexp 必选项.包含正则表达式模式或可用标志的正则表达式对象. str    必选项.要在其上测试查找的字符串. 说明 test 方法检查字符串是否与给出的正则表达式模式相匹配,如果是则返回 true,否则就返回 false. 每个正则表达式都有一个 lastIndex 属性,用于记录上一次匹配结束的位置. var

随机推荐