JavaScript闭包原理与实战:解决全局变量污染

作者:小括号

时间:2026-10-30

浏览:427

本文深入解析JavaScript闭包的概念、原理及实际应用。通过对比全局与局部变量作用域,阐述如何利用闭包避免全局变量冲突,并提供项目中的具体代码示例,帮助开发者写出更规范、安全的JavaScript代码。

文章目录

JavaScript闭包原理与实战:解决全局变量污染

本文通过具体实例详细解析JavaScript中闭包的使用方法,供读者参考学习。

引言

引言:为何需要闭包

在公司开发中,曾需要编写一个用于网站统计的JavaScript脚本,旨在实现类似百度统计或站长统计的功能。在开发过程中,自认为代码逻辑尚可,因为之前的JavaScript代码编写习惯如此。然而,在组长进行代码审查时,得到了非常不满意的评价。主要原因在于,我们在JavaScript中定义的方法均为全局方法。由于这些代码需要嵌入到第三方界面中,这些全局命名极有可能与宿主页面的其他代码发生重名冲突,从而引发错误。因此,组长给出了一条关键建议:使用JavaScript闭包将代码包裹起来。

变量作用域基础

变量作用域

我们熟知,变量的作用域主要分为全局变量和局部变量两类。在JavaScript中,函数内部可以直接读取全局变量。

Js代码


var n=999;
function f1(){
  alert(n);
}
f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

Js代码


function f1(){
  var n=999;
}
alert(n); // error

需要特别注意,在JavaScript中声明变量时若不使用var命令,实际上会声明一个全局变量。


function f1(){
  n=999;
}
f1();
alert(n); // 999

如何从外部读取函数内部变量

如何从外部读取全局变量?

如果我们希望获取函数内部的全局变量,该怎么办呢?在正常情况下这是无法直接实现的。要实现这一目标,我们需要采取一些特殊手段——即在函数内部再定义一个函数:


function f1(){
  n=999;
  function f2(){
    alert(n); // 999
  }
}

在上面的代码中,函数f2被包含在函数f1内部。此时,f1内部的所有局部变量对f2都是可见的。但反过来则不成立,f2内部的局部变量对f1是不可见的。这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。因此,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们是否就可以在f1外部读取它的内部变量了呢?


function f1(){
  n=999;
  function f2(){
    alert(n);
  }
  return f2;
}
var result=f1();
result(); // 999

闭包的概念解析

闭包的概念

在上面的代码中,f2函数即为闭包。各种专业文献对“闭包”(closure)的定义非常抽象,难以理解。我的理解是,闭包就是能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取局部变量,因此可以将闭包简单理解为“定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

项目中的闭包实战示例

下面来分享一下我们在项目中用到的js闭包的写法:


(function () {
  function getPageTitle() {
    return document.title;
  }
  function getBrowerLanguage() {
    var browerLanguage = !navigator.browserLanguage ? navigator.language : navigator.browserLanguage;
    return browerLanguage;
  }
  /**
   * 当前页面地址#后的部分
   */
  function getLastUrl() {
    var url = window.location.hash;
    if (!url) {
      return null;
    }
    else {
      return url.toString().split("#")[1];
    }
  }
  function GetRandomNum() {
    var arr = document.cookie.match(new RegExp("(^| )" + "statisticssCookie=([^;]*)(;|$)"));
    if (arr != null) {
      return arr[2];
    } else {
      var tempRandomNum = guid();
      document.cookie = "statisticssCookie = " + tempRandomNum;
      return tempRandomNum;
    }
  }
  function guid() {
    function S4() {
      return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
  }
  function addJs() {
    var url = "http://localhost:10086/tongji/tongji/do?title=" + getPageTitle() + "&browerLanguage=" + getBrowerLanguage() + "&lastUrl=" + getLastUrl() + "&upFlag=" + GetRandomNum();
    var head = document.getElementsByTagName('head')[0];
    var js = document.createElement('script');
    js.type = 'text/javascript';
    js.src = url;
    head.appendChild(js);
  }
  window.statistics = addJs;//将addJs这个方法挂在window下面,这样在外界是可以访问的,否则外界永不能访问到我写的方法
  document.ready = addJs();//DOM树加载完成后执行
})(window)

总结与建议

小结

在开始的时候组长就问我会不会js闭包函数,因为如果不会闭包写不出很好的代码。写的代码都非常的粗糙,所以说我们在写代码的时候,不能仅仅满足功能实现,而且需要考虑一些其他方面的东西。当然js闭包在使用的时候也有一些弊端,所以我们在使用的时候也需要综合全面的信息考虑。

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript数据结构与算法技巧总结》、《JavaScript数学运算用法总结》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》及《JavaScript遍历算法与技巧总结》

希望本文所述对大家JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章