`
rdqwhr
  • 浏览: 14806 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

【转】javascript变量作用域、匿名函数及闭包

    博客分类:
  • Java
阅读更多
一、JavaScript变量作用域(scope)
首先需要明白的几个要点:
1.JavaScript的变量作用域是基于其特有的作用域链的。
2.JavaScript没有块级作用域。
3.函数中声明的变量在整个函数中都有定义。(就后面第三点的说明)
4 .所有在最外层定义(非函数体内定义)的变量都拥有全局作用域
5. 所有末定义直接赋值的变量,系统会自动声明为拥有全局作用域的变量
6. 所有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>
分享到:
评论

相关推荐

    JS匿名函数、闭包

    即使JavaScript中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量; 有权访问私有变量的公有方法叫做特权方法; 可以使用构造函数模式、原型模式...

    javascript笔记之匿名函数和闭包

    本文介绍了js匿名函数和闭包的相关内容,供大家参考...闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量 &lt;scri

    深入理解JavaScript 中的匿名函数((function() {})();)与变量的作用域

    在这里简单介绍一下:闭包是可以访问在函数作用域内定义的变量的函数。若要创建一个闭包,往往都需要用到匿名函数。 2、模拟块级作用域,减少全局变量。执行完匿名函数,存储在内存中相对应的变量会被销毁,从而...

    JavaScript 匿名函数(anonymous function)与闭包(closure)

    变量作用域 函数外部访问函数内部的局部变量 用闭包实现私有成员 引入 闭包是用匿名函数来实现。闭包就是一个受到保护的变量空间,由内嵌函数生成。“保护变量”的思想在几乎所有的编程语言中都能看到。 先看下 ...

    JavaScript 匿名函数和闭包介绍

    闭包:可访问一个函数作用域里的变量的函数; 一 匿名函数 // 普通函数 function box(){ // 函数名是box; return 'Lee'; } box(); // =&gt;Lee; 调用函数; // 匿名函数 function(){ // 匿名函数,会报错; return '...

    浅析JavaScript作用域链、执行上下文与闭包

    JavaScript 采用词法作用域(lexical scoping),函数执行依赖的变量作用域是由函数定义的时候决定,而不是函数执行的时候决定。以下面的代码片段举例说明,通常来说(基于栈的实现,如 C 语言) foo 被调用之后函数...

    深入理解javascript作用域和闭包

    作用域是一个变量和函数的作用范围,javascript中函数内声明的所有变量在函数体内始终是可见的,在javascript中有全局作用域和局部作用域,但是没有块级作用域,局部变量的优先级高于全局变量,通过几个示例来了解下...

    深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解

    函数表达式 1、JavaScript中定义函数有2钟方法:  1-1.函数声明: 代码如下:function funcName(arg1,arg2,arg3){ //函数体} ①name属性:可读取... ①匿名函数(anonymous function,或拉姆达函数):function关键字

    JavaScript 编写匿名函数的几种方法

    匿名函数可以有效控制变量作用域,构造闭包 (Closure),防止对全局变量造成污染。在 JavaScript 中,编写匿名函数,有以下几种方法.

    浅谈JavaScript的闭包函数

    在JavaScript中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆。  闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。由于要求它可以访问另一个函数的...

    学习javascript的闭包,原型,和匿名函数之旅

    本文通过示例给大家介绍javascript的闭包,原型,和匿名函数,具体详情请看下文。 一 .&gt;关于闭包 理解闭包 需要的知识 1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function readA(){ alert&#40;n&#41...

    Javascript基础加强

    1.javascript基础-基本语法 变量作用域 匿名函数 2.javascript基础-基本语法 js面向对象 闭包 数组 3.javascript Dom 获取元素id 动态注册事件 window对象 4.javascript Dom 动态创建元素 操作样式 5.javascript Dom...

    JS闭包、作用域链、垃圾回收、内存泄露相关知识小结

    闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量 闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成...

    浅谈JS封闭函数、闭包、内置对象

    一、变量作用域指的是变量的作用范围,javascript中的变量分为全局变量和局部变量 1、全局变量:在函数之外定义的变量,为整个页面公用,函数的内部外部都可以访问。 2、局部变量:在函数内部定义的变量,只能在定义...

    js实现序号递增,嵌入到可执行的html,并给出注释

    闭包是JavaScript中一种非常重要的概念,它允许在函数内部创建一个独立的作用域,并将该作用域中的变量和函数保存起来,使得它们可以在函数外部被访问。在本例中,我们使用了一个自执行的匿名函数来创建一个闭包,将...

    JavaScript 闭包机制详解及实例代码

    首先要区分两个概念,一是匿名函数,一是闭包。 所谓匿名函数,就是创建函数没有给定函数名。经常出现的包括函数表达式,就是定义一个匿名函数,然后将函数赋值给某个变量,而此时这个变量就相当于该函数的函数名,...

    JavaScript 闭包详细介绍

    《JavaScript高级程序设计》上面这么描述的:闭包是指有权访问另一个函数作用域中的变量的函数。这句话第一次看的时候模模糊糊,似是而非。碰到问题就不会运用了,听别人的分析头头是道,说到底还是没搞明白。现在我...

    JavaScript知识点总结(十六)之Javascript闭包(Closure)代码详解

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。很早就接触过闭包这个概念了,但是一直糊里糊涂的,没有能够弄明白JavaScript的闭包...一、变量的作用域  要理解闭包,

    javascript 函数及作用域总结介绍

    在js中使用函数注意三点:1、函数被调用时,它是运行在他被声明时的语法环境中的; 2、函数自己无法运行,它总是被对象调用的,函数运行时,函数体内的this...} 2、 声明匿名函数 function(a,b){ return a+b;}匿名函

Global site tag (gtag.js) - Google Analytics