乐趣区

关于javascript:js中的函数嵌套和闭包

明天就先和大家一起聊一聊我了解的闭包。在聊这个问题之前,先理解一下变量的定义域。
在 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="#" id="size-12">12</a>
    <a href="#" id="size-20">20</a>
    <a href="#" 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…
大家还能够扫描二维码,关注我的微信公众号,蜗牛全栈

退出移动版