JavaScript-变量与作用域链

jQuery片段:

1 var

2     // Will speed up references to window, and allows munging its name.

3     window = this,

4     // Will speed up references to undefined, and allows munging its name.

5     undefined,

6     // Map over jQuery in case of overwrite

7     _jQuery = window.jQuery,

8     // Map over the $ in case of overwrite

9     _$ = window.$,

10

11     jQuery = window.jQuery = window.$ = function( selector, context ) {

12         // The jQuery object is actually just the init constructor ‘enhanced‘

13         return new jQuery.fn.init( selector, context );

14     },

15

16     // A simple way to check for HTML strings or ID strings

17     // (both of which we optimize for)

18     quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,

19     // Is it a simple selector

20     isSimple = /^.[^:#\[\.,]*$/;

从这一节开始,我们剥掉jQuery的外衣,看看里面藏着些什么。前一节中曾经提及,如果单看外层的匿名函数,不看里面的实现的话,这个实现肯定不是闭包。但是,如果把jQuery的实现加上的话,这个肯定就是一种闭包应用。万丈高楼平地起,要了解闭包应用,就首先要了解它的基础。而这一节,我们遇到的片段,就是这个基础的所在——变量。(虽然这个片段包含众多知识点,但请容许我一个个慢慢分说。)

声明变量

变量的英文名为variable,其前三个字母正是我们在JS声明变量的关键字——var。那么,我们先来看一下如何去声明一个变量:

1 /*

2  * 声明变量的格式为

3  * var 变量名 初始化变量表达式列表(可选)

4  */

5 var a=1, b, c="test", d=a+b;// 虽然b还没有初始化,但是声明是合法的

6 alert(a);// "1"

7 alert(b);// "undefined"

8 alert(c);// "test"

9 alert(d);// "NaN"

10 alert(e);// 这里将引发编译错误:"e"未定义

11 // 同样地,如果在初始化中使用未定义的变量,也会引发编译错误。

如上例所示,声明变量需要使用var关键字,然后在空格后紧跟变量的名字。在声明变量的同时,我们也可以选择帮变量初始化。初始化的值可以是任何类型的值或表达式,但是,如果你尝试使用未定义的变量名来初始化,JS的编译器将会判定发生编译错误,并阻止程序继续往下运行。无论你是否对声明的变量进行初始化,你都可以继续声明第二个变量而无须使用var关键字。你所需要的只是运算符“,”。(关于运算符将在稍后章节详细讨论。)但当你没有对声明的变量进行初始化时,变量将会被赋予值“undefined”——undefined也是JS的固有类型之一。PS:JS中使用的运算符必须是半角的英文字符,甚至空格也一样。

重复声明的变量?!

当我们声明了一个变量,而又在后续的代码中再次对他进行声明,结果会怎么样呢?或许在很多其他语言中,这都会引起重复定义的错误,但在JS中,这完全是合法的。并且,由于JS是弱数据类型,所以变量能被赋予任何类型的值。请看以下例子:

1 var a=1;

2 alert(typeof a); // "number"

3 var a;

4 alert(typeof a); // "number"

5 var a="1";

6 alert(typeof a); // "string"

7 a=new String("1");

8 alert(typeof a); // "object"

看完上面的例子,你可能会产生两个疑问:

a)为什么第二个a还是number?

b)为什么第四个a是object?

为了解答第一个问题,我们首先要了解声明一个变量到底是怎么运作的。而第二个问题,我们将他放到下一节再讨论。

var 变量声明的工作步骤

当我们使用var关键字去声明变量的时候,JS解释器将会进行如下操作:

1)预编译javascript代码块中所有非函数块内的var关键字;

2)生成变量名标识并在其所在作用域分配空间;

3)按代码顺序运行至第一个var关键字所在行;

4)按变量声明列表表达式次序计算初始化表达式的值;

5)每计算完一条初始化表达式,就将其计算结果赋予给对应的声明变量;

6)继续运行后续代码至下一var关键字;

7)重复4-7步到代码块结束;

8)继续编译运行下一个代码块。

PS:JS将以一个代码块,也就是一个script标签为单位去运行一段JS代码。

正是因为var的工作方式,实际上程序执行时,解释器是根本看不到var关键字的。他执行的只是初始化表达式的赋值语句而已——所以问题a的答案就是例子中的第三句实际上什么事也没有做。所以,你一点也不用为代码中会否出现重复定义的变量名而烦恼。你真正需要担心的是,初始化语句所产生的变量的值的变化是否如你预期。除此之外,请不要尝试使用保留字作为变量名。这几乎在所有语言中都必须遵循的规范。

另外,在函数块中声明变量的工作步骤也是类似的,但不同的是,他们是在函数运行时才创建的。

没有var的变量声明?!

很多朋友都应该有这个经验——“我们根本不需要使用var来声明变量也能直接赋值啊!”。这是因为JS解释器在遇到赋值表达式的时候,会先在作用域链中寻找这个变量是否已经声明。如果这个变量没有声明,则隐式强制为其在全局(Global)作用域中声明,并将表达式的值赋予给该变量。

但究竟为什么会这样呢?其实一切都源自于变量的获取规则和作用域链的化合作用外加赋值运算符的催化作用。

作用域链

每个运行时的上下文都有与其对应的一个作用域。而作用域链正是把这些作用域连接起来的桥梁。它的作用与程序寻找某一变量标识有关:

1)JS解释器会按调用的顺序把作用域加进作用域链(像栈般早进入的作用域会在作用域链的底部);

2)然后在程序寻找某一变量标识时进入作用域链中的第一个索引,并在其中寻找该变量标识;

3)如果没有找到该标识,则前往下一个索引继续寻找;

4)如果已经找到该标识,则将该标识及其值返回;

5)当搜索到最后一个索引仍未能找到该标识,则在最后的索引上创建该标识,并使其值为null,最后返回该标识与值。

PS:而上述的第5步发生的前提是该标识处于赋值运算符表达式左侧。

因此,当你没有使用var声明变量而直接使用对该变量作初始化操作(简单赋值)时,JS会自动为你创建该空值标识,并让它可以顺利执行赋值语句。

变量与作用域链

从上面的描述,我们可以很轻易的看到变量与作用域链的关系。因为只要程序需要寻找变量,就必须通过作用域链。而前面所谈及的闭包问题正是由此而来的。回想一下我们前面的示例代码:

1 var abc=function(y){

2 var x=y;// 这个是局部变量

3 return function(){

4   alert(x++);// 就是这里调用了闭包特性中的一级函数局部变量的x,并对它进行操作

5   alert(y--);// 引用的参数变量也是自由变量

6 }}(5);// 初始化

7 abc();// "5" "5"

8 abc();// "6" "4"

9 abc();// "7" "3"

10 alert(x);// 报错!“x”未定义!

时间: 2024-09-29 07:56:41

JavaScript-变量与作用域链的相关文章

[从jQuery看JavaScript]-变量与作用域链

jQuery片段: [javascript] view plaincopy var // Will speed up references to window, and allows munging its name. window = this, // Will speed up references to undefined, and allows munging its name. undefined, // Map over jQuery in case of overwrite _jQ

PHP (20140510)深入浅出 JavaScript 变量、作用域和内存 v 0.5

深入浅出 JavaScript 变量.作用域和内存 v 0.5 本文主要从原理入手分享变量和作用域的相关知识,最后结合本文所分享知识,再次深入了解下闭包的运行原理. 主要参考<JS高级程序设计> <JS权威指南> <高性能 JS> 三本书. 目录 1 变量 1.1 变量的声明 1.2 变量类型的特点 2 执行环境和作用域 3 再谈谈闭包 变量 对 JavaScript 稍微有点了解的同学都知道,JavaScript 中的变量与其他语言的变量有很大区别. JS 的弱类型的

JavaScript中的作用域链和闭包

JavaScript中的作用域链和闭包 2012-06-29 11:41 1878人阅读 评论(46) 收藏 举报 JavaScript中出现了一个以前没学过的概念——闭包.何为闭包?从表面理解即封闭的包,与作用域有关.所以,说闭包以前先说说作用域. 作用域(scope) 通常来说一段程序代码中使用的变量和函数并不总是可用的,限定其可用性的范围即作用域,作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突. 全局作用域(Global Scope) 在代码中任何地方都能访问到的对象拥

关于“javascript变量的作用域”的理解

*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* BLOCKS =============================================================================*/ p, blockquote, ul, ol, dl, table, pre { margin: 15px 0; } /* HEAD

转:javascript变量的作用域

原文请访问http://www.cnblogs.com/rubylouvre/archive/2009/08/21/1551270.html, 我也只是为了做个记录 javascript变量的作用域 <script type="text/javascript">  var a=100;  var b=true;  function test(){      alert(a);      alert(b);      b=false;      alert(b);     

【Python全栈-JavaScript】JavaScript变量的作用域

JavaScript变量的作用域全局变量 1.当在script中定义变量,在当前script中的定义前调用是undefined,没有定义该变量时调用报错.原因是在内存中开辟了该变量的存储位置,(当执行script时将所有定义的变量名先存到栈中) 当代码解释到该语句时才会将值存在栈中,没有执行之前调用该变量就是undefined,如果没有定义就不会在内存中开辟存储位置,因此就会报错. 2.如果在上一个script中调用下面的script的变量时,这时候不但没有值,而且没有开辟存储空间.因此,在上一

JavaScript中的作用域链原理

执行环境 作用域链的形成与执行环境(Execution Environment)相关,在JavaScript当中,产生执行环境有如下3中情形: 1 进入全局环境 2 调用eval函数 3 调用function 在一个执行环境A上可以创建执行环境B,执行环境B又可以创建执行环境C...,这一系列的执行环境构成执行环境栈,最新创建的执行环境位于栈顶(栈底永远是全局执行环境),当栈顶执行环境结束之后(与之相关的代码执行结束)就会被弹出站外,底下的执行环境就会成为新的栈顶.如下图所示: 一个执行环境由3

JavaScript 变量的作用域

一. 基本类型和引用类型 基本类型值指的是简单的数据段,而引用类型值指的是那个可能由多个值组成的对象   将一个值赋值给变量时,javascript解析器首先要确定是基本类型还是引用类型,基本数据类型可以直接操作保存在变量中的值,而引用数据类型的值是保存在内存中的对象,在操作对象时,实际上操作的是对象的引用而不是实际的对象 二. 变量的赋值 1. 如果从一个变量上向另一个变量上复制__基本数据类型__的值,会在变量对象上创建一个新值,然后把该值复制到新变量的位置上,这个很好理解,来看下例子: v

JavaScript 变量、作用域和内存问题

按照ECMA-262第三版的定义,JavaScript的变量与其他语言的变量有很大区别.JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何种数据类型的规则,变量的值及其数据类型可以在脚本的生命周期内改变.尽管从某种角度看,这可能是一个既有趣又强大,同时又容易出问题的特性,但JavaScript变量实际的复杂程度还远不止如此. 1,基本类型和引用类型的值 ECMAScript变量可能包含两种不同类型的值:基本类型值和引用类

javascript变量、作用域和内存问题......

1基本类型是指那些保存在栈内存的简单数据段,引用类型是指那些保存在堆内存中的对象,变量中保存的实际上只是一个指针. 2javascript中5种基本数据类型Undefined,Null,Boolean,Number和String 3事实上我们不能给基本类型的值添加属性,尽管在javascript这样做不会导致任何错误. var name = "Angela";name.age = 23;alert(name.age);//undefined; 4ECMAScript中所有函数的参数都是