jacascript 立即执行函数(IIFE)与闭包
前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!
一直没搞清楚立即执行函数和闭包之间的关系,总结一下:
- 闭包有很多种理解:访问不到内部作用域,函数就是这样,所以函数就是闭包;
- 闭包还有一种理解:通过把函数内部的变量和方法返回出来,这样外部作用域就可以访问内部作用域了,并且
- 立即执行函数和闭包之间没有必然的联系,虽然它们经常结合一起使用;
- 立即执行函数只是一种函数的调用方式;
- 闭包的目的则是外部函数可以访问内部函数的作用域;
立即执行函数(IIFE)
大牛的讲解,点击
立即执行函数、立即执行表达式、IIFE(immediately invoked function expression)、自执行函数,叫法不同,都是一样的;
立即执行函数是指声明完之后便直接执行的函数,这类函数通常是一次性使用的,因此没必要给这类函数命名,直接让它执行就好了;
主要目的是做的一些封装,防止变量全局污染,以及保证内部变量的安全;
javascript中 没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据 javascript 函数作用域链的特性,可以使用 IIFE 可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以 ( function(){…} )() 内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
JQuery 使用的就是这种方法,将 JQuery 代码包裹在 ( function (window,undefined){…jquery代码…} (window) 中,在全局作用域中调用 JQuery 代码时,可以达到保护 JQuery 内部变量的作用。
我们首先需要有函数表达式的概念,点击
定义一个函数,函数后面加上括号,即可完成调用;
<script type="text/javascript">
function (){}(); // SyntaxError: Unexpected token (
//引擎在遇到关键字function时,会默认将其当做是一个函数声明,函数声明必须有一个函数名,所以在执行到第一个左括号时就报语法错误了;
//函数表达式的函数名则可有可无;
//在function前面加!、+、 -、=甚至是逗号等或者把函数用()包起来都可以将函数声明转换成函数表达式;我们一般用()把函数声明包起来或者用 =
</script>
引擎在遇到关键字 function 时,会默认将其当做是一个函数声明,函数声明必须有一个函数名,所以在执行到第一个左括号时就报语法错误了;
然后我们给它加上函数名;
<script type="text/javascript">
function abc(){}(); // SyntaxError: Unexpected token )
//在一个表达式后面加上括号,表示该表达式立即执行;
//而如果是在一个语句后面加上括号,该括号完全和之前的语句没什么关系,而只是一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。
</script>
在一个语句后面加上括号,该括号完全和之前的语句没什么关系,而只是一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。上面代码可以写成:
<script type="text/javascript">
function abc(){};
();// SyntaxError: Unexpected token )
//分组操作符内的表达式不能为空,执行到右括号时,发现表达式为空,所以报错。
</script>
分组操作符内的表达式不能为空,执行到右括号时,发现表达式为空,所以报错。
然而函数表达式的函数名则可有可无;
在 function 前面加!、+、 -、=甚至是逗号等或者把函数用()包起来都可以将函数声明转换成函数表达式;我们一般用()把函数声明包起来或者用 = ;
我们在函数后面加上括号,然后再用另一个括号把它们都包起来;
javascript 中,括号内不允许包含语句,但可以是表达式;
引擎先遇到括号,然后遇到关键字 function , 就自动把括号里面的代码识别为函数表达式,而不是函数声明;
<script type="text/javascript">
(function (){/*code*/}());
//javascript中,括号内不允许包含语句,但可以是表达式;
//引擎先遇到括号,然后遇到关键字function , 就自动把括号里面的代码识别为函数表达式,而不是函数声明
(function(){/*code*/})(); //也可以这样写 var a = function(){/*code*/}();
//我们可以在函数表达式后面直接加括号,而不用把函数包起来; //但还是推荐外部加上():
var b = (function(){/*code*/}());
var c = (function(){/*code*/})();
//因为外部如果没有(),我们得去function(){/*code*/}后面看是否存在(),判断b/c是一个函数还是一个函数内部的返回值
//为了代码的可读性,还是要在外部加上(),无论是否已经是表达式;
</script>
javascript 只有函数拥有局部作用域,立即执行函数也有这一特点,我们可以利用它减少全局变量造成的空间污染;
<script type="text/javascript">
(function abc(){
console.log(abc);//function abc(){...}
var a = 1;
console.log(a);//1
})()
// console.log(a); //ReferenceError: a is not defined
console.log(abc);//ReferenceError: abc is not defined
//立即执行函数的函数名在外部也是找不到的
//函数表达式的标识符在外部作用域是找不到的,只有内部作用域可以找到
</script>
闭包(closure)
大牛的讲解,点击
我们首先需要有作用域的概念,点击
闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作;简单的说,外部作用域就可以访问函数内部作用域的变量了。
由于作用域的关系,我们在函数外部是无法直接访问到函数内部的变量的,但是函数内部可以把这个变量传给全局变量或者返回出来,这样外部作用域就可以访问函数内部作用域的变量了;
简单的说,闭包就是有权限访问另一个函数内部作用域的变量的函数;
- javascript 具有自动垃圾回收机制,函数运行完之后,其内部的变量和数据会被销毁;
- 但是闭包就是在外部可以访问此函数内部作用域的变量,所以闭包的一个特点就是只要存在引用函数内部变量的可能,JavaScript 就需要在内存中保留这些变量。而且 JavaScript 运行时需要跟踪这个内部变量的所有外部引用,直到最后一个引用被解除(主动把外部引用赋为 null 或者页面关闭),JavaScript 的垃圾收集器才能释放相应的内存空间;这句话不是很好理解,下面用代码展示;
<script type="text/javascript">
function outer(){
var a = 1;
function inner(){
return a++;
}
return inner;
}
var abc = outer();
//outer()只要执行过,就有了引用函数内部变量的可能,然后就会被保存在内存中;
//outer()如果没有执行过,由于作用域的关系,看不到内部作用域,更不会被保存在内存中了; console.log(abc());//1
console.log(abc());//2
//因为a已经在内存中了,所以再次执行abc()的时候,是在第一次的基础上累加的 var def = outer();
console.log(def());//1
console.log(def());//2
//再次把outer()函数赋给一个新的变量def,相当于绑定了一个新的outer实例; //console.log(a);//ReferenceError: a is not defined
//console.log(inner);//ReferenceError: a is not defined
//由于作用域的关系我们在外部还是无法直接访问内部作用域的变量名和函数名 abc = null;
//由于闭包占用内存空间,所以要谨慎使用闭包。尽量在使用完闭包后,及时解除引用,释放内存;
</script>
立即执行函数能配合闭包保存状态。
<script type="text/javascript">
for(var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i); //3 3 3
//在执行到这一行时,发现匿名函数里没有i,然后向往外部作用域找,然后找到的其实是for循环执行完了的i,也就是2++,3
},0);
}; for(var i = 0; i < 3; i++){
setTimeout((function(x){
console.log(x); //0 1 2
})(i),0);
//在立即执行函数内部i传给了x,并且锁在内存中,所以不会变
};
</script>
插件
用立即函数配合闭包写插件,防止变量全局污染,以及保证内部变量的安全;
<script type="text/javascript">
var Person = (function(){
var _sayName = function(str){
str = str || 'shane';
return str;
}
var _sayAge = function(age){
age = age || 18;
return age;
} return {
SayName : _sayName,
SayAge : _sayAge
}
})(); //通过插件提供的API使用插件
console.log(Person.SayName('lucy')); //lucy
console.log(Person.SayName());//shane
console.log(Person.SayAge());//18
</script>
一道经典面试题
下面的ul中,如何点击每一个 li 的时候弹出其下标?
<ul>
<li>index 00000</li>
<li>index 11111</li>
<li>index 22222</li>
</ul>
方法一:用闭包
<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
(function(index){
oLi[index].onclick = function(){
console.log(index);
};
})(i);
}
}
</script>
方法二:闭包还有一种写法
<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
oLi[i].onclick = (function(index){
return function(){
console.log(index);
}
})(i);
}
}
</script>
方法三:将下标作为对象的一个属性,添加到每个数组元素中,(name: " i ", value: i 的值);
<script type="text/javascript">
window.onload = function(){
var oLi = document.getElementsByTagName('ul')[0].children;
for (var i = 0; i < oLi.length; i++){
oLi[i].i = i;
oLi[i].onclick = function(){
console.log(this.i);
};
}
}
</script>
还有其他方法...以后在整理;
jacascript 立即执行函数(IIFE)与闭包的更多相关文章
- 立即执行函数(IIFE)的理解与运用
作为JavaScript的常用语法,立即执行函数IIFE(Immediately-Invoked Function Expression)是值得我们认真去学习探究的. 一.创建函数的两种方式 我们先从 ...
- 立即执行函数(function(){})()与闭包
立即执行函数 匿名(function(){})() 当一个匿名函数被括起来,然后再在后面加一个括号,这个匿名函数就能立即运行起来. $(function(){}) $(function(){});是$ ...
- 立即执行函数 IIFE
立即执行函数表达式IIFE(Immediately-invoked function expression)我们知道,在javascript(ES5)中,是没有块级作用域的概念的.看一个例子 for ...
- (译)详解javascript立即执行函数表达式(IIFE)
写在前面 这是一篇译文,原文:Immediately-Invoked Function Expression (IIFE) 原文是一篇很经典的讲解IIFE的文章,很适合收藏.本文虽然是译文,但是直译的 ...
- JS立即执行函数表达式(IIFE)
原文为 http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife ----------------- ...
- 立即执行函数(IIFE)
立即执行函数(IIFE) 看到这里,相信你一定迫不及待地想知道究竟如何做了吧,其实很简单,只需要用括号全部括起来即可,比如下面这样: (function(){ /* code */ }()); 为什么 ...
- 【JavaScript专题】--- 立即执行函数表达式
一 什么是立即执行函数表达式 立即执行函数表达式,其实也可以叫初始化函数表达式,英文名:IIFE,immediately-inovked-function expression.立即执行函数表达式就是 ...
- javascript的立即执行函数
在javascript中有引入立即执行函数的概念,那么什么是立即执行函数呢?立即执行函数又是怎么写的呢?立即执行函数与普通函数有什么区别呢? 先来看看一般的函数: function a(){ var ...
- 立即执行函数表达式(IIFE)
原文地址:benalman.com/news/2010/11/immediately-invoked-function-expression/ 译者:nzbin 也许你还没有注意到,我是一个对术语比较 ...
随机推荐
- 笔记:Spring Cloud Hystrix Command属性
主要用来控制 HystrixCommand 命令的行为,主要有下面5种类型的属性配置: execution配置 该配置前缀为 hystrix.command.default execution.iso ...
- java中的notify和notifyAll有什么区别?
先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入 ...
- python开发装饰器的应用
python全栈开发-Day10 装饰器(闭合函数的应用场) 一. 装饰器 装饰器就是闭包函数的一种应用场景 什么是闭包函数?我们再来回忆一下: 闭包函数: 定义在函数内部的函数,并且该函数包含对 ...
- KS检验统计量的扩展应用(CMap)
KS检验统计量的扩展应用 KS(Kolmogorov-Smirnov)检验是比较两个经验分布之间是否存在差异. 我们设X1, X2,-, Xm, Y1, Y2,-, Ym为两个独立随机样本,分别满足假 ...
- MySQL数据库操作类(PHP实现,支持连贯操作)
<?php /** * Author: suvan * CreateTime: 2018/2/27 * description: 数据库操作类(仅对接MySQL数据库,主要利用MySQLi函数) ...
- img之间的间隙问题
前言:关于基线(base line),中线(middle line),行高(line height)的了解还是比较浅的,所以引用前辈的成果,稍带解释下 1)行高:两行文字之间"基线" ...
- Archlinux下i3wm与urxvt的配置
前段时间学习了GitHub的两位前辈:Airblader和wlh320.他们的相关教程在https://github.com/Airblader/i3和https://github.com/wlh32 ...
- 从PRISM开始学WPF(六)MVVM(三)事件聚合器EventAggregator?
从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? ...
- EXT3文件系统误删除导致文件系统中的邮件丢失恢复方法
一.故障描述 由8块盘组成的RAID5, 上层是EXT3文件系统,由于误删除导致文件系统中的邮件丢失 二.镜像磁盘为防止数据恢复过程中由于误操作对原始磁盘造成二次破坏, 使用winhex软件为每块磁盘 ...
- day-1 用python编写一个简易的FTP服务器
从某宝上购买了一份<Python神经网络深度学习>课程,按照视频教程,用python语言,写了一个简易的FTP服务端和客户端程序,以前也用C++写过聊天程序,编程思路差不多,但是pytho ...