js进阶——作用域闭包

news/2024/9/22 23:03:32 标签: javascript, 开发语言, ecmascript, 前端, web
webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

1. 作用域与闭包的基础概念

1.1 作用域 (Scope)

在 JavaScript 中,作用域定义了变量、函数的可访问性。根据变量声明的位置不同,作用域有三种主要类型:

  • 全局作用域:在代码的任何地方都可以访问。
  • 函数作用域:只在声明的函数内部访问。
  • 块作用域:由 {} 包围的代码块内部可访问,使用 letconst 声明的变量具备块作用域。
1.2 闭包 (Closure)

闭包是指一个函数与其外部(函数定义时)环境的结合,使得即使函数在外部作用域被调用,它依然能够访问其创建时的变量。这是 JavaScript 中的一个关键特性,用于实现封装、私有变量、回调函数等。

简单地说,闭包就是内部函数可以“记住”它的外部函数的变量,即使外部函数已经执行完毕

2. 作用域链与闭包的关系

每个 JavaScript 函数在执行时都会创建一个作用域链(scope chain)。作用域链用来确保函数执行时能访问所有变量。这个作用域链由多个作用域逐层嵌套形成,最里层的作用域是函数自己的局部作用域,往外是外部函数的作用域,直到全局作用域。

闭包的关键在于,函数的作用域链不会因为外部函数执行完毕而丢失。当内部函数被执行时,它依然能够通过作用域链访问外部函数的变量,这就是闭包的核心机制。

3. 闭包的使用场景

3.1 封装与私有变量

闭包常用来创建私有变量,外部函数不能直接访问这些变量,只有通过闭包暴露的接口才能操作它们。

javascript">function createCounter() {
  let count = 0; // 私有变量
  return function() {
    count++; // 通过闭包访问和修改
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

在这个例子中,count 变量虽然在 createCounter 函数中定义,但由于返回的函数形成了闭包,它依然能访问 count

3.2 模拟块级作用域

letconst 出现之前,JavaScript 没有块级作用域,闭包可以用来模拟这种效果。

javascript">for (var i = 0; i < 3; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i); // 0, 1, 2
    }, 100);
  })(i);
}
3.3 回调函数与异步操作

在 JavaScript 中,异步操作和事件处理器广泛使用闭包。常见场景包括定时器、事件监听器、Ajax 回调等。

javascript">function asyncTask(message) {
  setTimeout(function() {
    console.log(message); // 通过闭包保留 `message`
  }, 1000);
}

asyncTask('Hello, Closure!');

4. 闭包的优缺点

4.1 优点
  • 数据封装:通过闭包实现私有变量,防止数据污染。
  • 状态保持:闭包能够“记住”外部函数的变量状态,适合场景如计数器、缓存等。
4.2 缺点
  • 内存泄漏:由于闭包保留了对外部变量的引用,可能导致垃圾回收无法及时清除变量,造成内存泄漏。如果闭包中存在大量无用变量,可能占用较多内存。
  • 调试困难:闭包使得变量的作用域更加复杂,调试代码时需要仔细追踪变量的作用域链。

5. 高级闭包技巧

5.1 自执行函数表达式 (IIFE)

自执行函数表达式(Immediately Invoked Function Expression, IIFE)是通过闭包来实现的,将函数定义后立即执行。

javascript">(function() {
  let privateVar = 'This is private';
  console.log(privateVar);
})();

通过这种方式,privateVar 是局部变量,不会污染全局命名空间。

5.2 函数工厂

闭包还可以用于创建函数工厂,根据不同的输入生成不同的行为。

javascript">function multiplier(factor) {
  return function(num) {
    return num * factor; // 闭包保存 `factor`
  };
}

let double = multiplier(2);
console.log(double(5)); // 10

let triple = multiplier(3);
console.log(triple(5)); // 15
5.3 函数记忆化

闭包可以实现函数的记忆化(memoization),用于缓存计算结果,优化性能。

javascript">function memoize(fn) {
  let cache = {};
  return function(...args) {
    let key = JSON.stringify(args);
    if (!cache[key]) {
      cache[key] = fn(...args); // 缓存计算结果
    }
    return cache[key];
  };
}

function slowFunction(num) {
  let result = num * 2; // 假设这是个耗时计算
  return result;
}

let memoizedSlowFunction = memoize(slowFunction);
console.log(memoizedSlowFunction(5)); // 10
console.log(memoizedSlowFunction(5)); // 10 (缓存结果)

6. 闭包与性能

由于闭包会保留引用到外部作用域的变量,错误使用可能造成内存泄漏。因此,在使用闭包时应当注意以下几点:

  1. 避免不必要的闭包:仅在需要状态保持时使用闭包。
  2. 及时释放闭包:在闭包不再需要时,将其引用置为 null,以便垃圾回收。
  3. 避免过度嵌套:深度嵌套的闭包链会增加性能开销。

总结

闭包是 JavaScript 中非常强大且常用的特性,它结合了作用域链的机制,可以保存函数执行时的上下文环境,用于实现私有变量、回调函数、状态保持等多种功能。理解闭包及其工作原理,有助于更好地编写高效、结构清晰的 JavaScript 代码。


http://www.niftyadmin.cn/n/5670950.html

相关文章

Linux(6)--CentOS目录

文章目录 1. 根目录2. cd目录切换命令3. CentOS目录介绍4. pwd命令介绍5. ls命令介绍5.1 ls5.2 ls -a5.3 ls -l 1. 根目录 Windows电脑的根目录是计算机(我的电脑)&#xff0c;然后C盘、D盘。 Linux系统的根目录是/&#xff0c;我们可以使用cd /进入根目录&#xff0c;然后使…

OpenCV特征检测(10)检测图像中直线的函数HoughLinesP()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在二值图像中使用概率霍夫变换查找线段。 该函数实现了用于直线检测的概率霍夫变换算法&#xff0c;该算法在文献 181中有所描述。 HoughLines…

javase笔记4----常用类型

常用类型 包装类 简介 java语言是面向对象的语言&#xff0c;但是其中的八大基本数据类型不符合面向对象的特征。 因此java为了弥补这样的缺点&#xff0c;为这八种基本数据类型专门设计了八种符合面向对象特征的的类型&#xff0c;这八种具有面向对象特征的类型&#xff0c;…

【工具】Windows|两款开源桌面窗口管理小工具Deskpins和WindowTop

总结 Deskpins 功能单一&#xff0c;拖到窗口上窗口就可以置顶并且标记钉子标签&#xff0c;大小 104 KB&#xff0c;开源位置&#xff1a;https://github.com/thewhitegrizzli/DeskPins/releases WindowTop 功能完善全面强大&#xff0c;包括透明度、置顶、选区置顶等一系列功…

《ImageNet Classification with Deep Convolutional Neural Networks》总结

摘要 训练了一个深层的卷积神经网络&#xff0c;在图像分类的竞赛上以碾压性的优势击败了第二名。有5个卷积层&#xff0c;一些池化层&#xff0c;3个全连接层。用了dropout防止过拟合 1、引言 在几十年前&#xff0c;神经网络不被计算机视觉接受&#xff0c;他们认为这种黑…

javascript的闭包学习

为什么要产生闭包的概念&#xff0c;通俗来说一下。 公司有一个项目&#xff0c;分为两个部分&#xff0c;张三、李四各分配一个部分。 张三.js代码&#xff1a; var key我要吃肉 function fn(){console.log(key); } 李四.js代码&#xff1a; var key我要喝酒 function fn…

Python学习——【4.3】数据容器:str字符串

文章目录 【4.3】数据容器&#xff1a;str字符串一、再识字符串二、字符串的下标&#xff08;索引&#xff09;三、字符串的常用操作四、字符串的遍历五、字符串的特点 【4.3】数据容器&#xff1a;str字符串 一、再识字符串 虽然之前已经学习过字符串&#xff0c;但此处我们需…

408算法题leetcode--第11天

3. 无重复字符的最长子串 3. 无重复字符的最长子串思路&#xff1a;滑动窗口时间&#xff1a;O(n)&#xff1b;空间&#xff1a;O(字符种类数) class Solution { public:int lengthOfLongestSubstring(string s) {// 滑动窗口&#xff1a;如果没有出现相同的字符&#xff0c;…