ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

详解javascript变量作用域、匿名函数及闭包

2021-04-13 20:56:54  阅读:156  来源: 互联网

标签:闭包 function 函数 show 作用域 javascript alert var


一、JavaScript变量作用域(scope)

首先需要明白的几个要点:

1.JavaScript的变量作用域是基于其特有的作用域链的。

2.JavaScript没有块级作用域。

3.函数中声明的变量在整个函数中都有定义。(就后面第三点的说明)

4 .所有在最外层定义(非函数体内定义)的变量都拥有全局作用域

  1. 所有末定义直接赋值的变量,系统会自动声明为拥有全局作用域的变量

  2. 所有window对象的属性拥有全局作用域

(下面作者还提到全局变量都可以通过window.*来访问,明白这一点很重要,但是否准确?此外目前存在的疑问还有:

即使可以以这中方式来访问全局变量那是否能认为全局对象由window来统一管理,还是window只是保存了一个拷贝而已?)

1.JavaScript的作用域链

首先看一个很简单的例子,下面的代码中,定义一个全局变量n,n的作用域是全局的。

<script language="javascript" type="text/javascript">

var n=1;

function show(){

var m=2;

alert(n);  //显示结果为1

}

show();

</script>

运行代码后,结果为1。原理就是JavaScript作用域是层层包含的,外层的作用域在内层有效,执行的时候,从内层向外层查找。当函数show执行的时候,首先在函数show内部查找变量n,没有找到,再向外层查找,找到了全局变量n,alert显示结果为1。

2.局部变量优先级高于全局变量

<script language="javascript" type="text/javascript">

var n=1;

function show(){

var m=2;

var n=3;

alert(n);  //显示结果为3

}

show();

</script>

这个不难理解,根据上面变量作用域链的原理,函数执行时,会先从内层向外层查找,如果在内层中找到变量,查找就会停止。所以内层的作用域会优先于外层,局部优先于全局。

稍改一下:

<script language="javascript" type="text/javascript">

var n=1;

function show(){

var m=2;

var n=3;

alert(window.n);  //显示结果为1

}

show();

</script>

为什么会是1,而不是3呢,只是另外一个问题:全局变量都是window对象的属性,任何的全局变量都是window对象的属性,都可以用window.*来引用。(*代表一个变量)

3.在函数内部使用var声明的变量,不论在何处声明,该变量都拥有整个函数的作用域。

<script language="javascript" type="text/javascript">

var n=1;

function show(){

alert(n);  //undefined

alert(this.n); //显示为1

var n=3;  //声明局部变量n

alert(n);  //显示为3

}

show();

</script>

第一个问什么会显示undefined呢,这个问题的原因就是在函数后面声明了局部变量n=3,在函数show执行的时候,会先从内层查找变量n,结果找到了,但在第一个alert的时候,n还没有赋值,故结果为undefined。

4.JavaScript没有块级作用域

<script language="javascript" type="text/javascript">

var n=1;

function show(){

for(var i=0;i<3;i++){

var n=6;

alert(i);  //显示结果为0,1,2

}

alert("for循环外:"+i); //显示结果为:“for循环外:3”

alert("n="+n);  //显示结果为:n=6

}

//alert(i);  外部引用一个未声明变量会提示出错

show();

</script>

变量i虽然是在for循环内定义的,但for循环块外部仍然可以引用,i的作用域是整个函数show。如果在show函数外部引用i变量,会出现JavaScript报错。

5.没有使用var声明的变量属于全局变量

<script language="javascript" type="text/javascript">

var n=1;

function show(){

m=9;  //声明变量m

var s=6;

}

show();  //不可少,alert之前需要先执行m=9,否则m未声明

alert(m);  //显示9

alert(window.m);  //显示9

alert(s);  //报错"s is not defined"

</script>

函数show执行的过程中,引用了变量m,但未使用var声明,m就变成了全局变量。

二、JavaScript匿名函数

所谓匿名函数就是没有命名的函数,看起来有点难以理解,其实很常见,下面举一个小例子。

window.onload=function(){ alert("加载完毕!");}

这样就创建了一个匿名函数,这个function没有函数名,但可以被调用和执行,当网页加载完毕时,会执行alert()。

<script language="javascript" type="text/javascript">

(function(x,y){alert(x+y)})(2,3);  //显示结果为5

</script>

上面的方法仍然是创建了一个匿名函数,并使用()优先表达式强制执行函数。

三、JavaScript闭包(closure)

闭包其实就是利用了函数作用域和匿名函数的知识,当函数A执行结束时,一部分变量变量被B引用,被引用的变量不能释放,形成了所谓的闭包。这里有篇很好的文章,可以参考一下。

下面看一个小例子:

<script language="javascript" type="text/javascript">

function show(){

var n=3;

setTimeout(function(){alert("first:"+n);},3000); //3秒后显示first:3

alert("second:"+n); //显示second:3

}

show();

function show2(){alert("第一个函数执行结束");}

show2();

</script>

运行上面的函数后,首先alert显示"second:3",然后显示"第一个函数执行结束",3秒后显示“first:3”。

仔细看一下,函数show执行结束的时候,变量n应该被释放了,内存里也就不存在了,怎么过了3秒还能显示出"first:3"呢?闭包恰恰就在此产生了,当函数show执行结束的时候,想要释放内存里的n,但发现变量n还在被一个匿名函数引用,要在3秒后调用,根据JavaScript闭包原理,内存里的n被保留了下来,于是在三秒以后仍然可以调用n。

下面看几种写法的区别:

function show2(content){

alert(content);  //3秒后alert显示"test"

}

function show(content,delay_time){

setTimeout("show2('"+content+"')",delay_time);

}

show("test",3000);

仔细看一下上面这种写法不是闭包,只是普通的函数调用而已。

function show(content,delay_time){

setTimeout(function(){alert(content)},delay_time); //3秒后alert显示"test"

}

show("test",3000);

这种写法就是闭包,虽然结果是一样的,但原理不一样。

function show(content){

function show2(){alert(content);}

return show2;

}

var newshow=show("test");

setTimeout(newshow,3000);  //3秒后alert显示"test"

上面这种写法也是闭包,虽然结果都是一样的,但原理却不一样。这里面看到了函数也可以当作对象也传递,函数show执行后return show2,把函数show2赋值给newshow,然后3秒后,调用newshow对象,执行函数show2。

仔细比较上面3种写法,如果能悟出一些道理就好,闭包有些时候的确难理解。

稍复杂一点的例子:

<script language="javascript" type="text/javascript">

var show=null;

(function(){

var n=1;

var show2=function(){

alert(n++);

}

show=show2;

})();

show();  //显示1

show();  //显示2

show();  //显示3

</script>

上面代码中,又一次用了函数作为对象传递,n是匿名函数里的局部变量,外部是访问不到的。show是一个全局变量,应该访问不了变量n,但在函数show执行前,首先把函数匿名函数内部show2赋值给show,当后面函数show运行的时候,会调用show2的代码,而show2是可以访问n的,故结果显示为1、2、3。

另外一点值得注意的,也是很经典的一个例子。

CSS代码:

ul{ width:600px; margin:0px auto;}

ul li{ margin:1px 0px; background-color:#999999;}

HTML代码:

<ul>

<li>1</li>

<li>2</li>

<li>3</li>

<li>4</li>

</ul>

JavaScript代码:

<script language="javascript" type="text/javascript">

var li=document.getElementsByTagName("li");

for(var i=0;i<li.length;i++){

li[i].onclick=function(){alert(i);}

}

</script>

运行后,不论点击哪一个li,都是alert提示“4”。这就是一个需要注意的地方:闭包允许内层函数引用父函数中的变量,但是该变量是最终值。闭包引用的变量i,是循环结束后的值,其实这是一个很常见的问题,解决方法也有很多。

比较常见的就是给li[i]添加属性值,比如li[i].n=i;还有就是用闭包方法解决,这个函数本身就是闭包,所谓用闭包来解决闭包的问题。代码如下:

<script language="javascript" type="text/javascript">

var li=document.getElementsByTagName("li");

for(var i=0;i<li.length;i++){

(function(index){

li[index].onclick=function(){alert(index);}

})(i);

}

</script>

 

标签:闭包,function,函数,show,作用域,javascript,alert,var
来源: https://blog.51cto.com/u_15166492/2704011

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有