乐趣区

关于javascript:JS知识点梳理之作用域作用域链柯里化闭包

一、作用域与作用域链

作用域是指 js 变量应用时所存在的一个区域,分为全局作用域(window)和部分作用域(function、setTimeout… 等都会产生部分作用域)。当部分作用域变量名与全局作用域变量名反复时,局部变量会笼罩全局变量。

在部分作用域应用变量时,如果在本人作用域找不到对应变量,则会往上一级作用域查找,直到全局作用域,如果全局作用域无此变量则会报 undefined。相同,全局作用域中无奈应用部分作用域中的变量。

window.a = 1
function(){
  // 输入 1,尽管部分没有 a 变量,然而 全局中有。console.log(a)
  var b = 2
}
// 报错,全局中无奈应用局部变量。console.log(b)

下面这种一层层向外查问变量的过程叫做查问作用域链。而这种一层层部分作用域直到全局作用域的构造被称为作用域链。

// 全局作用域,申明了一个全局变量 a
var a = 100

// 函数会生成部分作用域
function acs(){
  // 在此部分作用域中申明一个局部变量 b
    var b = 50
  // 输入:100, 50
  console.log(a, b) // 执行过程:在此作用域查找变量 a,// 找不到 --> 往上一级作用域找 --> 在全局找到,应用全局作用域中的 a
                                      // 在此作用域查找变量 b,查找到了,应用此局部变量的 b
}()

// 输入:b is not defined
console.log(a, b)

二、闭包(Closure)

1. 闭包是什么?

闭包是指在函数内部调用函数外部的局部变量,且在调用后局部变量不会被浏览器立刻回收,会始终存在的一种公有变量。再简略点说就是函数返回函数。

红宝书中的刻画:闭包是指有权拜访另一个函数作用域中的变量的函数。

其实闭包就是返回一个函数,且这个函数对局部变量存在援用造成的蕴含关系就是闭包。

其实就是创立一个不会被 GC 回收的局部变量。也正因如此,闭包才会有内存透露的危险,须要在每次应用完后立即革除。

闭包的造成:以后环境中存在指向父级作用域的援用。

2. 闭包的写法

// 应用自执行函数造成闭包
var add = function(){
    let sum = 0
  return function operation(){return sum = sum ? sum + 1 : 1}
}()

// 输入:1
add()
// 输入:2
add()
// 输入:3
add()
// 输入:4
add()

// 革除闭包,删除公有变量
add = null
// 输入:null
console.log(add)
// 输入:add is not function
add()

3. 闭包的作用

应用闭包的目标――暗藏变量,间接拜访一个变量,在定义函数的词法作用域外,调用函数。

闭包通常在回调函数、公有属性、函数柯里化中应用。

4. 应用闭包实现多个图片点赞性能

应用闭包实现,多图点赞独自点赞性能,且每个 input 的点赞数量互不烦扰。在这个例子中利用闭包申明了 5 个新的独立词法作用域。

<!-- * @Description: 闭包实现多图点赞 * @Author: CY 小尘 s * @Date: 2021-07-28 18:39:33 * @LastEditTime: 2021-11-08 17:19:37 * @LastEditors: Please set LastEditors-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title> 闭包实现多图点赞 </title>
  </head>
  <style>
    ul li {list-style: none;      float: left;      margin: 0px 20px 20px 0px;}    ul li img {width: 200px;      height: 200px;}  </style>
  <body>
    <ul>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-0.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-1.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-2.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-3.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-6.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
      <li>
        <img
          class="img"
          src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-7.jpg"
          alt="你好"
        />
        <input type="button" class="add" value="以后点赞数量(1)" />
      </li>
    </ul>

    <script>
      window.onload = function () {        // 获取 ul
        let add = document.querySelectorAll("ul li input");        // 循环取出每个 input 增加上闭包
        for (let i = 0; i < add.length; i++) {          // 给每个 input 增加点击事件
          add[i].onclick = (function () {            let sum = 2;            // 返回函数,实现公有变量的创立,造成闭包
            return function () {              this.value = "以后点赞数量(" + sum++ + ")";};          })();}      };    </script>
  </body>
</html>

5. 应用闭包爱护公有属性

创立一个计数器函数,在外面定义一个公有属性,这里通过闭包爱护它不会被间接批改。

能够看见在这个例子中咱们并没有间接操作 privatelyCounter,而是通过 makeCounter 被动裸露的办法来操作计数器中的 privateCounter。

// 创立一个计数器
const makeCounter = function(){
    // 创立公有变量
    var privatelyCounter = 0
    // 输入公有变量
    function console(){return privatelyCounter}
    // 更改计数器办法
    function change(num){privatelyCounter += num}
    // 裸露私有办法
    return {CounterAdd(num){change(num)
        },
        CounterSub(num){change(num)
        },
        CounterLog(){return console()
        }
    }
}
// 申明两计数器
const counter1 = makeCounter()
const counter2 = makeCounter()
counter1.CounterAdd(1)
counter1.CounterAdd(1)
counter2.CounterSub(-1)
counter2.CounterSub(-1)
// 输入:2
console.log(counter1.CounterLog())
// 输入:-2
console.log(counter2.CounterLog())

参考视频解说:进入学习

三、应用闭包实现函数柯里化

所谓函数柯里化就是将一个多参函数转为单参函数。

// 失常求自增办法
function numAdd(x, y){return x + y}
console.log(numAdd(1, 2))

// 应用闭包实现柯里化
function numAddCurry(x){return function(y){return x + y}
}
// 先申明一个变量拿到自增办法
const curry = numAddCurry(1)
// 在调用这个变量进行自增,输入:3
console.log(curry(2))
// 亦或者间接调用自增办法传入两个参数,输入也是:3
console.log(numAddCurry(1)(2))
退出移动版