JavaScript要理解闭包先了解词法作用域

一系列JavaScript基础概念是连通的
服务器君一共花费了255.819 ms进行了6次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

之所以取名叫做词法作用域,是这个概念是js中相当基础也是极为重要的,很多想当然的错误或感觉怪异的问题都是和这个东西有关。所以,本文主要说下这个名词的概念以及讨论下他牵扯出来的有关变量、函数、闭包的问题。

由变量开始谈

习惯性先来段代码:

var x = "globol value";
var getValue = function()
{
	alert(x);    //弹出"undefined"
	var x = "local value";
	alert(x);    //弹出"local value";
}
getValue();

代码很简单,首先定义了一个全局变量x并赋了初值,然后写了个getValue的方法,之后我们用alert弹出x的值,但是结果是undefined,不是global value也不是local value,这个我们可能会感觉到奇怪。其实理解这个问题的关键就是要清楚x的作用域。

第一个var x中的x是全局变量,说到这里顺带说下,js解释器在执行任何代码之前会先创建一个全局对象(global object),全局变量就是相当于这个全局对象的一个属性。同理,对于getValue这个函数,就会生成一个叫做调用对象的东西,局部变量就是这个调用对象的属性,例子中第二个var x中的x就是局部变量。

主角登场

说了以上的东西其实是为了将本文的主角——词法作用域。这个是何方神圣呢?要理解的话,其实我们可以对比传统面向对象的(如JAVA、C#)中的变量的作用域,我们知道C#中的变量作用域是块级的,即这个变量只能活动在定义他的一个直接外界内,如if子句内,for子句内定义的变量外界是无法读取的。而js中呢,变量却不是这样的,在同一个函数内定义的变量其它的成员是可以访问的。看个例子会清楚很多:

function test(o)
{
	var i = 0;
	if(typeof o == "object")
	{
		var j = 0;                    
		for(var k=0; k < 10; k++)
		{
			document.write(k);
		}
		document.write(k);            //k是可以被访问到的,即使他在for子句内
	}
	document.write(j);                //说明j是可以被访问到的,即使他在if子句内
}

清楚了这一点后,就来理解下js中关于词法作用域的含义。当定义了一个函数后,当前的作用域就会被保存下来,并且成为函数内部状态的一部分,这个是很重要的一个概念。下面我们回到开篇的那个例子,当getValue函数被定义的时候,他的作用域被保存起来,还有被加到作用域链上,他的上端就是全局执行环境。当调用getValue方法的时候,js解释器首先会把作用域设置为定义函数的时候的那个作用域(即之前保存那个),接下来,他在作用域的前加上调用对象即getValue这个函数,再在他的上端加上全局对象。晕了没?还是看下我画的图把:

这样比较清楚了把,这个作用域链其实和原型链有点相似,也好似在很作用域内找不到就会向上去找。比方说开篇那个例子,找x的时候,(对了这里要先介绍下js的预定义机制,就是js解释器会先对var定义的变量进行初始化,应该说只是起了定义的作用当没赋值),会先在本作用域内找,有预定义知可以找到x,但是没赋值,所以是undefined值。知道了这点我们来知道开篇那个代码其实是等价于下面这个的:

var x = "globol value";
var getValue = function()
{
	var x;
	alert(x);    //弹出"undefined"
	x = "local value";
	alert(x);    //弹出"local value";
}
getValue();

实际上js解释器做的事情应该是按以上这个例子执行的,所以从另一个角度说,将变量的定义放在开头这个约定是有意义并且有益处的。

延伸

清楚了以上关于词法作用域的概念后,我们就不难理解闭包的概念了,他只是用到了作用域链的不可向下性(我取的名词..),即下面的作用域可以访问上面的,但上面的不可以访问下面的。当然这只是构成闭包的一个条件,闭包更重要的还是外部函数持有内部函数的一个嵌套函数的引用,由于闭包不是本文主要要讨论的(ps:谈封装的时候会说到),所以只是简单看下例子:

function foo()
{
	var age = 10;
	function boo()
	{
		age += 10;
		return age;
	}
	return boo;
}
 
var tx = new foo();
alert(tx());    //20

词法作用域的讨论就先这样了,我想说得是这个概念还是相当重要的。当然本文的内容也只是我针对书本介绍的自我的一些看法和理解,有不得当的地方希望各位指出。

本文地址:http://www.nowamagic.net/librarys/veda/detail/1307,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/1307

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

大家都在看

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《UNIX环境高级编程(第2版)》 史蒂文斯 (作者), 拉戈 (作者), 尤晋元 (译者), 张亚英 (译者), 戚正伟 (译者)

《UNIX环境高级编程(第2版)》是被誉为UNIX编程“圣经”的Advanced Programming in the UNIX Environment一书的更新版。在本书第1版出版后的十几年中,UNIX行业已经有了巨大的变化,特别是影响UNIX编程接口的有关标准变化很大。本书在保持了前一版的风格的基础上,根据最新的标准对内容进行了修订和增补,反映了最新的技术发展。书中除了介绍UNIX文件和目录、标准I/O库、系统数据文件和信息、进程环境、进程控制、进程关系、信号、线程、线程控制、守护进程、各种I/O、进程间通信、网络IPC、伪终端等方面的内容,还在此基础上介绍了多个应用示例,包括如何创建数据库函数库以及如何与网络打印机通信等。

更多计算机宝库...