本文深入解析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程序设计有所帮助。