学习JavaScript中的闭包closure应该注意什么

 更新时间:2022年06月07日 09:31:46   作者:​ 灵扁扁   ​  
这篇文章主要介绍了学习JavaScript中的闭包closure应该注意什么?在 JavaScript 中, 每当创建一个函数, 闭包就会在函数创建的同时被创建出来,但是学习的时候我们应该注意哪些问题呢,带着疑问一起进入下面文章学习具体内容吧

闭包简述

Mozilla 上这样解释闭包:一个函数和对其周围状态(lexical environment,词法环境) 的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。 也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中, 每当创建一个函数, 闭包就会在函数创建的同时被创建出来。 词法(lexical)一词指的是,词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。

我对闭包的理解:闭包使得可以模拟私有项,可以使得内部函数可以访问外部函数的属性,非必要不用闭包。

1.闭包使得内部函数可以访问外部函数的属性(变量或方法)

这有时会带来便利, 例如有时可以通过在外部函数声明变量,代替全局变量。 下面是一个设备视口大小改变时,重置 echarts 的例子。

// 设备视口大小改变时,重置 echarts
let timer = null
window.onresize = function () {
  // 简单的防抖动处理
  if (timer) clearTimeout(timer)
  timer = setTimeout(() => {
    console.log(timer)
    chart.resize()
  }, 500)
}

也可以考虑使用闭包的方式,而不必在声明全局变量(更大范围的变量) timer,例如这样

window.onresize = this.debounce(() => {
  chart.resize()
}, 500)

function debounce (fn, delay = 500) {
  let timer = null
  return (p) => {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn(p)
    }, delay)
  }
}

2.闭包的广阔应用场景

闭包的广阔应用场景,体现在你使用只有一个方法的对象的地方,都可以使用闭包。

因为闭包允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。 在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。

而在日常开发中,符合使用闭包的场景其实很常见,因为使用只有一个方法的对象的地方,都可以使用闭包, 而使用也并不太麻烦,加上闭包本身就是 javascript 的重要知识点,这些加起来使得闭包具备了实用的特征。

但如果你不熟练闭包,有更好的替代方案,也不必非要使用,因为实用好用的东西很多, 闭包只是选择之一,为了给自己多一种选择闭包又是要学的。

3.用闭包模拟私有方法

JavaScript 没有类似 JAVA 那样的将方法声明为私有的原生支持,但我们可以使用闭包来模拟私有方法。 私有方法不仅仅有利于限制对代码的访问,还提供了管理全局命名空间的强大能力, 避免非核心的方法弄乱代码的公共接口部分。

下面的示例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为模块模式(module pattern)

window.onload = () => {
  let Counter1 = makeCounter(); // 创建实例1
  let Counter2 = makeCounter(); // 创建实例2

  console.log(Counter1.value()); // value:0
  Counter1.add(); // 调用增加函数,执行加一
  console.log(Counter1.value()); // value:1

  console.log(Counter2.value()); // value:0

  // 注意,实例2的 value 没有受到实例1的影响,也就是说 Counter1 和 Counter2 各自独立。
  // 每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。
  // 然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。

  // undefined,Counter1 无法直接访问私有项 privateNumber
  console.log(Counter1.privateNumber);
  // Counter1.changeBy is not a function,Counter1 无法直接访问私有项 changeBy
  console.log(Counter1.changeBy(10));

  // 问私有项无法被访问,这提示我们应关注到以这种方式使用闭包,
  // 提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。
}

/// 声明一个模块:计数器,模块内部包含了两个模拟的私有项 privateNumber 和 changeBy,
// 并返回一个对象,对象内部包含三个属性,分别是 add(),reduce(),value()。
let makeCounter = function () {
  let privateNumber = 0;

  function changeBy (val) {
    privateNumber += val;
  }

  return {
    add: function () {
      changeBy(1);
    },
    reduce: function () {
      changeBy(-1);
    },
    value: function () {
      return privateNumber;
    }
  }
};

在这个例子中,包含两个私有项: 名为 privateCounter 的变量和名为 changeBy 的函数。 这两项都无法在函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。这就是模拟了私有特性。

4.从性能角度考虑,非必要不使用闭包

关于闭包的性能,我无深入的理解,也无数据证明,但我认为这挺重要的。

因此,这里引用一下 Mozilla 的说法:

如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的, 因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。 原因是这将导致每次构造器被调用时,方法都会被重新赋值一次(也就是说,对于每个对象的创建,方法都会被重新赋值)。

英文原文:

It is unwise to unnecessarily create functions within other functions if closures are not needed for a particular task, as it will negatively affect script performance both in terms of processing speed and memory consumption.

For instance, when creating a new object/class, methods should normally be associated to the object's prototype rather than defined into the object constructor. The reason is that whenever the constructor is called, the methods would get reassigned (that is, for every object creation).

概括就是一句话,非必要不用闭包。好东西很多闭包只是之一, 当然闭包作为js的重要知识点,作为可能的解决方案之一,学习是必要的。

到此这篇关于学习JavaScript中的闭包closure应该注意什么的文章就介绍到这了,更多相关JS 闭包closuret内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js实现简单拼图游戏

    js实现简单拼图游戏

    这篇文章主要为大家详细介绍了js实现简单拼图游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • JS获取表格视图所选行号的ids过程解析

    JS获取表格视图所选行号的ids过程解析

    这篇文章主要介绍了JS获取表格视图所选行号的ids过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 分析JS中this引发的bug

    分析JS中this引发的bug

    这篇文章主要介绍了分析JavaScript中this引发的bug以及相关的处理方法分析,一起学习下吧。
    2017-12-12
  • JavaScript遍历json对象数据的方法

    JavaScript遍历json对象数据的方法

    这篇文章介绍了JavaScript遍历json对象数据的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • javascript打开新窗口同时关闭旧窗口

    javascript打开新窗口同时关闭旧窗口

    因业务需要,在网上查找这个问题的解决办法,但是昏天黑地地搞了半天,找到的方法虽然可以实现功能,但是总是会跳出讨厌的“关闭窗口”的提示框,郁闷。
    2009-01-01
  • Js 弹出框口并返回值的两种常用方法

    Js 弹出框口并返回值的两种常用方法

    有时候我们需要在新窗口执行一些代码并讲求将执行的结果返回到这个页面,那么就需要下面的方法,js常用的就是下面这中方法。
    2010-12-12
  • JavaScript实现的鼠标跟随特效示例【2则实例】

    JavaScript实现的鼠标跟随特效示例【2则实例】

    这篇文章主要介绍了JavaScript实现的鼠标跟随特效,结合2则实例形式分析了javascript针对鼠标事件的响应、计算、处理及页面元素动态操作相关实现技巧,需要的朋友可以参考下
    2018-12-12
  • JS清除字符串中重复值的实现方法

    JS清除字符串中重复值的实现方法

    这篇文章主要介绍了JS清除字符串中重复值的实现方法,涉及javascript数组与字符串的遍历、比较及数学运算相关技巧,需要的朋友可以参考下
    2016-08-08
  • JS组件Bootstrap实现下拉菜单效果代码

    JS组件Bootstrap实现下拉菜单效果代码

    这篇文章主要为大家详细介绍了JS组件Bootstrap实现下拉菜单效果代码,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • JS基于开关思想实现的数组去重功能【案例】

    JS基于开关思想实现的数组去重功能【案例】

    这篇文章主要介绍了JS基于开关思想实现的数组去重功能,简单分析了开关思想的原理,并结合具体实例形式分析了javascript基于开关思想实现数组去重相关操作技巧,需要的朋友可以参考下
    2019-02-02

最新评论