文档章节

JS词法作用域

hming
 hming
发布于 2016/11/30 17:15
字数 1118
阅读 20
收藏 2

1.由变量开始谈
习惯性先来段代码:
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就是局部变量。
2.词法作用域
词法作用域。这个是何方神圣呢?要理解的话,其实我们可以对比传统面向对象的(如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();
当调用 getValue()函数时形成的作用域链为“调用对象”->“全局对象”,执行alert(x)时,首先查找调用对象是否有x的属性,如果有则使用“调用对象”的x,如果没有,就接着查找“全局对象”是否有x的属性。
实际上js解释器做的事情应该是按以上这个例子执行的,所以从另一个角度说,将变量的定义放在开头这个约定是有意义并且有益处的。
3.延伸
清楚了以上关于词法作用域的概念后,我们就不难理解闭包的概念了,它只是用到了作用域链的不可向下性,即下面的作用域可以访问上面的,但上面的不可以访问下面的。当然这只是构成闭包的一个条件,闭包更重要的还是外部函数持有内部函数的一个嵌套函数的引用,看下简单例子:
function foo(){
    var age = 10;
    function boo(){
        age += 10;
        return age;
    }
    return boo;
}
alert(foo()());    //20

//var tx = foo();
//alert(tx());    //20

© 著作权归作者所有

共有 人打赏支持
hming
粉丝 3
博文 88
码字总数 86648
作品 0
深圳
程序员
为什么不建议使用eval和with?

面试官:为什么不建议使用eval和with? 因为影响性能、减低代码的安全性、代码更加难于阅读。 那为什么会影响性能?、为何会减低安全性?、怎么就让代码更加难于阅读? 这个些问题主要是考察...

tryzf
07/01
0
0
从上下文,到作用域(彩蛋:理解闭包)

前言 近几天在编程群中的聊天,让我发现了很多人并不清楚什么是上下文(context)、什么是作用域(scope),而且纠结在其中。我当初对这两个概念也只有粗浅的理解,不过我从一开始就不怎么困...

天方夜
07/04
0
0
【前端工程师手册】JavaScript之作用域

【前端工程师手册】JavaScript之作用域 什么是作用域 来一段《你不知道的JavaScript-上卷》中的原话: 几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个 值...

优惠券活动
05/17
0
0
【译】【nodeschool】【scope-chains-closures】作用域

作用域链与闭包工作 作用域,作用域链,闭包以及垃圾回收它们有一个共同点:那就是它们通常都是手动执行的。闭包实际上是如何工作的?垃圾回收在什么时候发生?作用域链到底是什么? 通过这次...

小草先森
05/14
0
0
你不知道的JavaScript·第一部分

第一章: 作用域是什么 1、 编译原理 JavaScript 被列为 ‘动态’ 或 ‘解释执行’ 语言,于其他传统语言(如 java)不同的是,JavaScript是边编译边执行的。 一段源码在执行前会经历三个步骤...

曾田生z
06/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

12-利用思维导图梳理JavaSE-多线程

12-利用思维导图梳理JavaSE-多线程 主要内容 1.线程概念 2.线程开发 3.线程的状态 4.线程的同步和死锁 5.Java5.0并发库类 QQ/知识星球/个人WeChat/公众号二维码 本文为原创文章,如果对你有一...

飞鱼说编程
17分钟前
0
0
JAVA集合之ArrayList

一、前言 Java 集合类提供了一套设计良好的支持对一组对象进行操作的接口和类,JAVA常用的集合接口有4类,分别是: Collection:代表一组对象,每一个对象都是它的子元素 Set:不包含重复元素...

木木匠
38分钟前
1
0
转:XMLHttpRequest2 新技巧

”XMLHttpRequest 的异步调用网上找的例子运行没问题,但稍微改了一点点就报错”InvalidStateError: XMLHttpRequest has an invalid context“。断断续续 搞了3天终于通了,可以接收二进制文...

SamXIAO
57分钟前
2
0
=====D服务器定时任务=====

Linux定时任务 crontab linux系统是有cron这个系统服务来控制的,Liunx系统上包含很多的计划性工作,使用者自己可以设置计划任务,所以linux系统提供了使用者控制计划任务的命令 crontab的启...

覃光林
今天
1
0
xilinx资源

本系列教学视频由赛灵思高级战略应用工程师带领你:从零开始,一步步深入 掌握 HLS 以及 UltraFAST 设计方法,帮助您成为系统设计和算法加速的大拿! http://www.eetrend.com/topics/2018-0...

whoisliang
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部