热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript模块化和闭包

JS模块化和闭包js最初作为一个在浏览器中运行的脚本语言,设计的目标是用来给html增加交互行为,早期的网站都是在服务器端生成并返回给浏览器࿰

JS模块化和闭包

js最初作为一个在浏览器中运行的脚本语言,设计的目标是用来给html增加交互行为,早期的网站都是在服务器端生成并返回给浏览器,js也只对单独的一个html进行操作,所以模块化并没有在早期的JS中得到很好的考虑,随着浏览器js引擎越发的快速,现在已经有很多前端框架,并不依赖与服务器生成html,而是自己直接通过js来生成,最典型的例子就是我们常听到的webapp。现在所有的js库都包装的非常好了,我们今天看看一些js模块化的基础知识吧。


名称空间namespace

在js中如何实现命名空间,我们来看一个例子。


<html><meta http-equiv&#61;"content-type" content&#61;"text/html;charset&#61;utf-8"><body><button id&#61;"mybtn" onclick&#61;"display();">点我加载列表button><h1>我想要不死族的英雄h1><ul id&#61;"mylist">ul> <script type&#61;"text/Javascript" src&#61;"./namespacejs1.js">script> <script type&#61;"text/Javascript" src&#61;"./namespacejs2.js">script> body>
html>

我们先定义一个html文件。点击里面的按钮&#xff0c;显示魔兽争霸的军队。响应按钮的代码分别定义在namespacejs1.js和namespacejs2.js两个文件中。
这是namespacejs1.js

var list &#61; [&#39;死亡骑士&#39;,&#39;巫妖&#39;,&#39;恐惧魔王&#39;];
function display(){var mylist &#61; document.getElementById("mylist"), fragment &#61; document.createDocumentFragment(), element; for(var i &#61; 0, x &#61; list.length; idocument.createElement("li"); element.appendChild( document.createTextNode( list[i]) ); fragment.appendChild(element); }console.log(importGlobalVariable);mylist.appendChild(fragment);
}

这是namespacejs2.js

var list &#61; [&#39;圣骑士&#39;,&#39;大法师&#39;,&#39;山丘之王&#39;];

当我们点击按钮的时候&#xff0c;会显示什么东东呢。我们期望显示不死族的英雄&#xff08;namespacejs1.js中的list&#xff09;&#xff0c;但是这里会显示人类的英雄&#xff08;namespacejs2.js中的list&#xff09;。显然&#xff0c;我们想要的数据被覆盖了。怎么样才能解决这个问题呢&#xff1f;我们来加上名称空间吧。看看改进后的namespacejs1.js。

var namespace1 &#61; {list : [&#39;死亡骑士&#39;,&#39;巫妖&#39;,&#39;恐惧魔王&#39;],display : function display(){var mylist &#61; document.getElementById("mylist"), fragment &#61; document.createDocumentFragment(), element; for(var i &#61; 0, x &#61; list.length; idocument.createElement("li"); element.appendChild( document.createTextNode( this.list[i]) ); fragment.appendChild(element); }mylist.appendChild(fragment);}
};

这时候点击按钮会没有用&#xff0c;需要稍微改动一下

<button id&#61;"mybtn" onclick&#61;"namespace1.display();">点我加载列表button>

可以看到display在namespace1的名称空间下面了。问题已经圆满解决。但是如果我们想扩展namespace1的功能怎么办呢&#xff0c;难道只能修改namespace1.js的源文件吗&#xff0c;还有list很不安全啊&#xff0c;谁都可以改动它&#xff0c;如何把它变成私有变量呢&#xff1f;


js的封装

在js中并没有私有公有的直接支持&#xff0c;但是不代表js语言不能完成这个。我们看一下如何隐藏list。

var namespace1 &#61; (function(){var importGlobalVariable &#61; gv;var list &#61; [&#39;死亡骑士&#39;,&#39;巫妖&#39;,&#39;恐惧魔王&#39;];function display(){var mylist &#61; document.getElementById("mylist"), fragment &#61; document.createDocumentFragment(), element; for(var i &#61; 0, x &#61; list.length; idocument.createElement("li"); element.appendChild( document.createTextNode( list[i]) ); fragment.appendChild(element); }mylist.appendChild(fragment);}return {display:display};
})();

这段代码返回了一个对象&#xff0c;在这个对象中我们只能访问display方法,是不是很牛逼呢&#xff0c;这样就解决隐藏问题。


js模块的扩展

我们知道js的继承是用原型链来实现的&#xff0c;但是这里要讨论的是模块的扩展&#xff0c;所以这边不会说道继承的问题。如何扩展namespace1的功能呢。我么看一下下面的代码。

var namespace1 &#61; (function(n1){var listArmy &#61; [&#39;4个蜘蛛&#39;,&#39;2个食尸鬼&#39;,&#39;2个冰龙&#39;]n1.displayArmy &#61; function(){n1.display();var mylist &#61; document.getElementById("mylist"), fragment &#61; document.createDocumentFragment(), element; for(var i &#61; 0, x &#61; listArmy.length; idocument.createElement("li"); element.appendChild( document.createTextNode( listArmy[i]) ); fragment.appendChild(element); }mylist.appendChild(fragment);}return n1;
})(namespace1);

这段代码我们用来增加一个displayArmy的方法&#xff0c;用来显示不死族军队&#xff0c;也就是listArmy的数据吧。
我们看到上面的代码把namespace1作为参数传入到一个立刻调用函数中&#xff0c;这样在里面给它增加一个函数。有没有感觉js很强大啊。


闭包&#xff08;Closures&#xff09;

如果已经习惯了C&#xff0c;C&#43;&#43;&#xff0c;C#或者Java&#xff0c;那么上面的实现简直匪夷所思&#xff0c;感觉变量的作用域和生命周期都很奇怪。那么我们说说js中的一个重要概念闭包吧。


定义


Closures are functions that refer to independent (free) variables.
这里就不翻译了&#xff0c;因为翻译过来实在是很奇怪&#xff0c;比如&#xff0c;闭包是那些引用了独立变量的函数。那么按照定义是否可以认为函数就是闭包呢&#xff0c;为了搞清楚闭包的概念&#xff0c;我们需要了解函数对象&#xff0c;变量生命周期&#xff0c;和嵌套的作用域三个概念。



函数对象

什么是函数对象呢&#xff0c;在C语言中和C&#43;&#43;语言中&#xff0c;可以想函数那样用()去调用&#xff0c;看起来和函数一样的对象就叫函数对象。比如在C语言中&#xff1a;

typedef void (*func_t)(int); //定义函数指针
//定义一个函数
void f(int n){printf("node(?)&#61;%d\n",n);
}
int main(){func_t pf &#61; f;f(1);
}

可以看到f很像一个函数吧&#xff0c;但是呢&#xff0c;其实它只是一个函数指针而已。在C&#43;&#43;语言中&#xff0c;我们也看一个例子。

class Foreach
{
private:struct node * myList;public:Foreach(){}~Foreach(){}void operator()(){//做点有意义的事情吧}
};
int main(){Foreach *foreach &#61; new Foreach();(*foreach)();
}

可以看到foreach对象也很像函数。
我们在看一下js中的函数对象吧。

var f &#61; function(){}&#xff1b;
f();

看看&#xff0c;是不是很简单。


嵌套的作用域

我们已经看到js的函数对象的定义已经很方便了&#xff0c;那么还有什么和传统语言不一样的地方呢&#xff1f;
我们看一下这个代码。

var x &#61; 10;
function f(){var y&#61;15;function g(){var z&#61;25;alert(x&#43;y&#43;z);}g();
}
f();//显示50

这段代码在C语言中没法实现&#xff0c;原因是C语言的变量无法访问当前作用域外的变量。也就是说函数g里面访问不了y&#xff0c;函数f也访问不了x。但是js却能做到。我们看一下这样有什么强大的地方。

#include
#include struct node{struct node *next;int val;
};// 函数指针&#xff0c;JS中的函数对象
typedef void (*func_t)(int); void foreach(struct node *list, func_t func){while(list){func(list->val);list &#61; list->next;}
}
void f(int n){printf("node(?)&#61;%d\n",n);
}
int main(){func_t pf &#61; f;f(1);// bool b &#61; false;// b &#61; true;// b &#61; "zifuchuan";struct node * list &#61; 0, *l;int i;for(i&#61;0; i<4; i&#43;&#43;){l &#61; malloc(sizeof(struct node));l->val &#61; i;l->next &#61; list;list &#61; l;}i&#61;0;l&#61;list;//这个循环可以打印出index&#xff0c;也就是i&#xff0c;如下是打印结果//node(0) &#61; 3//node(1) &#61; 2//node(2) &#61; 1//node(3) &#61; 0while(l){printf("node(%d) &#61; %d\n", i&#43;&#43;, l->val);l &#61; l->next;}//foreach里面再调用函数f&#xff0c;就不能访问i了&#xff0c;如下是打印结果//node(?)&#61;3//node(?)&#61;2//node(?)&#61;1//node(?)&#61;0foreach(list,f);
}

我们看到C语言的高阶函数&#xff08;函数调用函数&#xff09;是没法访问外部变量的。那么js写这段代码怎么弄呢&#xff1f;

function foreach(list, func){while(list){func(list.val);list&#61;list.next;}
}var i &#61; 0;
//这里可以使用变量i
foreach(list,function(n){console.log("node("&#43; i &#43;") &#61; " &#43;n);i&#43;&#43;;
});

我们发现js的变量作用域比C语言要牛逼一点吧。


生命周期

下面再看看js闭包的更牛逼的地方吧。

function extent(){var n&#61;0;return function (){n&#43;&#43;;console.log("n&#61;"&#43;n);}
}
f &#61; extent();
f();//&#61;>n&#61;1
f();//&#61;>n&#61;2

这里&#xff0c;当extent函数执行完毕后&#xff0c;n变量应该挂了才对&#xff0c;但是&#xff0c;我们通过结果看到&#xff0c;n变量还活的好好的呢。


总结

属于外部作用域的局部变量&#xff0c;被函数对象给“封闭”在里面了。闭包&#xff08;“closure”&#xff09;这个词原本就是封闭的意思。被封闭起来的变量的寿命&#xff0c;与封闭它的函数对象的寿命相等。也就是说&#xff0c;当封闭这个变量的函数对象不再被访问&#xff0c;被垃圾回收器回收掉时&#xff0c;这个变量才挂。
现在大家明白闭包的定义了吧。在函数对象中&#xff0c;将局部变量封闭起来的结构被叫做闭包。因此&#xff0c;C语言的函数指针并不是闭包&#xff0c;Javascript中的函数对象才是闭包。另外C&#43;&#43;等传统面向对象语言&#xff0c;也加入了对函数式编程的支持&#xff0c;其中一方面就是Lambda表达式&#xff0c;也有闭包的概念。


全局变量的导入

现在大家理解闭包的概念了&#xff0c;我们看看全局变量导入的问题。因为闭包中内部变量会一直持有外部变量&#xff0c;所以我们最好把外部变量当做参数传递给我们要使用的内部函数&#xff0c;这样会节省内存和查找变量的时间。因为找到一个外部变量&#xff0c;需要从内部往外部一层层的查找&#xff0c;很费时间&#xff08;对解析器来说&#xff09;。另外&#xff0c;一起开发代码的人也会很迷惑这个变量到底在那里定义的&#xff0c;容易出错&#xff0c;我们来看一个例子。

var globalVariable &#61; "我是外层变量,从disply函数找到我需要很久很久";var namespace1 &#61; (function(gv){var importGlobalVariable &#61; gv;var list &#61; [&#39;死亡骑士&#39;,&#39;巫妖&#39;,&#39;恐惧魔王&#39;];function display(){var mylist &#61; document.getElementById("mylist"), fragment &#61; document.createDocumentFragment(), element; for(var i &#61; 0, x &#61; list.length; idocument.createElement("li"); element.appendChild( document.createTextNode( list[i]) ); fragment.appendChild(element); }console.log(importGlobalVariable);mylist.appendChild(fragment);}return {display:display};
})(globalVariable);

这个例子把globalVariable当成参数传入&#xff0c;这样他就在匿名function函数的作用域里面了。


推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 深入理解CSS中的margin属性及其应用场景
    本文主要介绍了CSS中的margin属性及其应用场景,包括垂直外边距合并、padding的使用时机、行内替换元素与费替换元素的区别、margin的基线、盒子的物理大小、显示大小、逻辑大小等知识点。通过深入理解这些概念,读者可以更好地掌握margin的用法和原理。同时,文中提供了一些相关的文档和规范供读者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • JS实现一键分享功能
    本文介绍了如何使用JS实现一键分享功能,并提供了2019独角兽企业招聘Python工程师的标准。同时,给出了分享到QQ空间、新浪微博和人人网的链接。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
author-avatar
jgfioirejmf
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有