JavaScript-变量与作用域链
jQuery片段:
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。那么,我们先来看一下如何去声明一个变量:
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是弱数据类型,所以变量能被赋予任何类型的值。请看以下例子:
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会自动为你创建该空值标识,并让它可以顺利执行赋值语句。
变量与作用域链
从上面的描述,我们可以很轻易的看到变量与作用域链的关系。因为只要程序需要寻找变量,就必须通过作用域链。而前面所谈及的闭包问题正是由此而来的。回想一下我们前面的示例代码:
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”未定义!
JavaScript-变量与作用域链的更多相关文章
- [从jQuery看JavaScript]-变量与作用域链
jQuery片段: var // Will speed up references to window, and allows munging its name. window = this, // ...
- javascript变量的作用域
javascript变量的作用域 基本类型和引用类型 基本类型值指的是简单的数据段,而引用类型值指的是那个可能由多个值组成的对象 讲一个值赋值给变量时,javascript解析器首先要确定是基本类型 ...
- javascript的关键所在---作用域链
javascript的关键所在---作用域链 javascript里的作用域是理解javascript语言的关键所在,正确使用作用域原理才能写出高效的javascript代码,很多javascript ...
- javascript 函数初探 (三)--- javascript 变量的作用域
javascript 变量的作用域: 这是一个至关重要的问题.特别是当我们从别的语言转向javascript时,必须要明白一点,即在javascript中,变量的定义并不是以代码块作为作用域的,而是以 ...
- 深入理解 JavaScript 变量的作用域和作用域链
一个变量的作用域(scope)是程序源代码中定义这个变量的区域.简单的说,作用域就是变量与函数的可访问范围.全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义.局部变量是在函数体内 ...
- JavaScript 执行环境(执行上下文) 变量对象 作用域链 上下文 块级作用域 私有变量和特权方法
总结自<高程三>第四章 理解Javascript_12_执行模型浅析 JS的执行环境与作用域 javascript高级程序第三版学习笔记[执行环境.作用域] 在javascript ...
- javascript闭包和作用域链
最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...
- JavaScript变量和作用域
认识JavaScript中的变量 JavaScript中的变量有两种类型,一种是基本类型.一种是引用类型. 基本数据类型:Defined,Null,Boolean,Number,String.注意St ...
- javascript笔记:javascript的关键所在---作用域链
javascript里的作用域是理解javascript语言的关键所在,正确使用作用域原理才能写出高效的javascript代码,很多javascript技巧也是围绕作用域进行的,今天我要总结一下关于 ...
- Javascript——闭包、作用域链
1.闭包:是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式:在一个函数内部创建另一个函数. function f(name){ return function(object){ var ...
随机推荐
- C语言的问卷调查
1.你对自己的未来有什么规划?做了哪些准备? 未来想当一个网络工程师,为了这个目标我正在努力学习网络.网页及相关的知识. 2.你认为什么是学习?学习有什么用?现在学习动力如何?为什么? 学习就是不断尝 ...
- js实现轮播功能
先上图,效果大概就是这样子: 实现的功能: 1.鼠标经过第几个正方形,就要展示第几张图片,并且正方形的颜色也发生变化 2.图片自动轮播,(这需要一个定时器) 3.鼠标经过图片,图片停止自动播放(这需要 ...
- XHTML语法规范
<head> <meta charset="utf-8" /> <title>xhtml语法规范</title> </head ...
- [历史百科]抗战时期兵团简介 From 百度知道
中央军委1948年11月1日和1949年1月15日两次关于统一全军组织和部队番号的训令,我军先后进行了整编.西北野战军改称第一野战军,司令员兼政治委员彭德怀,第一副司令员张宗逊,第二副司令员赵寿山,参 ...
- Redis 请求应答模式和往返延时 Pipelining
Redis是一个CS结构的TCP服务器,使用”请求-应答”的模式.,客户端发起一个请求是这样的步骤: 客户端发送一个请求给服务器,然后等待服务器的响应,一般客户端使用阻塞模式来等待服务器响应. 服务器 ...
- idea tomcat debug 失效
idea 开发神器 有时候遇到各种问题 这不 现在遇到了一个问题 启动容器时 debug断点不能进入 在网上找了老半天 终于找到答案了 原因是使用tomcat的时候 没有选择"pass en ...
- 第189天:BOM属性方法
一.BOM---location 1.访问页面 location.href = "http://www.baidu.com"; location.assign("http ...
- debug - taotao项目 - IDEA拖动文件的自动重命名是超级巨坑, 一定要非常小心
大量的如下错误: org.springframework.beans.factory.BeanCreationException: Could not autowire field 还是要相信报错 不 ...
- [HDU5677]ztr loves substring
ztr loves substring Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Othe ...
- 【BZOJ2806】Cheat(后缀自动机,二分答案,动态规划,单调队列)
[BZOJ2806]Cheat(后缀自动机,二分答案,动态规划,单调队列) 题面 BZOJ 洛谷 题解 很有趣的一道题啊 对于在所有的串上面进行匹配? 很明显的后缀自动机 所以先构建出广义后缀自动机 ...