再探Javascript词法作用域

2016-02-19 16:15 5 1 收藏

想要天天向上,就要懂得享受学习。图老师为大家推荐再探Javascript词法作用域,精彩的内容需要你们用心的阅读。还在等什么快点来看看吧!

【 tulaoshi.com - Web开发 】

  写在前面的话:每个人都会犯错——有时候‘孰能无过,过而能改,善莫大焉’,有时候知道自己错了却没有机会更改。其实,错了并不仅仅是错了,做错了,除了及时改正和弥补之外,最重要的是为自己犯的错承担所有责任。

  2009年3月14日,我去参加网易互动的专场招聘会,应聘网页工程师的职位。有幸参加笔试,然后有幸栽在笔试,呵呵。废话少说,抓出音响极深的一题重新研究研究。

  题目大概是:写出如下代码的输出结果并进行分析

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/)

var tt = 'aa';
function test(){
    alert(tt);
    var tt = 'dd';
    alert(tt);
}
test();

  “太简单了!”这是我当时看到这个题目是的第一想法,于是轻率答题竟成我的致命之伤。我的答案是——aa和dd,解析:第一次输出全局变量的结果,然后局部变量tt覆盖全局变量所引用的值,所以第二次输出结果是dd。

  任何人见我如此作答,都会认为我是在扫盲——想法及其幼稚(我也这么认为)!

  网易啊,怎么可能会满意于这种答案!

  正确的答案应该是:undefined和dd

  为什么第一次alert的结果是undefined呢?要解释得清楚明白需要用到Javascript的词法作用域。

  Javascript中的函数“在定义它们的作用域里运行,而不是在执行它们的作用域里运行”,这是权威指南里抽象而精辟的总结。

  Javascript的逻辑默认在一个全局作用域中执行,如以上程序段中的“var tt='aa';”就是定义一个全局作用域的全局变量(如果以上代码段不是摘自某个函数链的话)。而test()函数内部的逻辑必须在原有的作用域(全局作用域)链再添加test函数本身的作用域(局部性)——这些思想几乎在每一种语言中都是如此定义的,然而Javascript作用域链的特别之处在于函数内部能够嵌套函数的定义(这是闭包的基础。注:在JS中函数是唯一形式的代码作用域)

  嵌套的内部函数可以调用外部函数(被嵌套的函数)的变量和其他嵌套函数(函数是一种数据)。如果是在外部函数内调用嵌套函数,那么调用对象不变,当外部函数执行完毕后所有数据(包括外部函数和嵌套的内部函数)都将被垃圾回收机制收集——这一点还不能体现出‘闭包’的精华。有一种情况,就是Javascript允许外部调用嵌套的内部函数,即使被嵌套函数已经被‘垃圾收集’——最常见的就是在‘某个函数’中用其嵌套的内部函数定义某些元素的响应事件,页面载入的时候被嵌套函数(‘某个函数’)已经执行完毕(被垃圾回收),但当事件触发的时候仍然会有响应的动作,而且响应函数中还可能调用到在被嵌套函数(‘某个函数’)中定义的变量最终值(不是被垃圾回收了吗?)。

  关于闭包的知识和示例有很多资料可供查询,我不想叙述。

  本文的重点是以下非常重要的细节:

  调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量)、函数参数及Arguments对象都在函数内的作用域中——这意味着它们隐藏了作用域链更上层的任何同名的属性。

  即,在以上程序片段中,test函数内部的“var tt='dd'”将会致使“var tt='aa'”在test函数被调用时完全被隐藏。而且,tt是在第一个alert语句之后定义,所以在调用到第一个alert时,tt是还没有被赋值的。这样说可能会清楚一点,即,在定义test函数时,当定义第一个alert(tt)时,这里会记录tt是作用域链中的一个变量但不会记录它(tt)的值,函数定义完毕后tt就添加到作用域里,所以第一个alert语句能够找到该作用域里的tt(即,相当于找到一个已经在函数内部声明,但未被赋值的tt)。


  以上程序片段的执行结果与以下片段的结果相同:

var tt = 'aa';
function test(){
    var tt;
    alert(tt);
    tt = 'dd';
    alert(tt);
}
test();

  Javascript的作用域不可简单的用C++等语言的思维来理解啊!C++在调用函数之前必须先声明或定义,而Javascript没必要。在Javascript中可以先调用函数,后再定义(不用在调用之前作任何声明)。因为在调用函数时,Javascript是向作用域链要函数的定义(函数在定义它们的作用域里运行,而不是在执行它们的作用域里运行)

  如以上代码写成:

var tt = 'aa';
test();  //先调用后再定义
function test(){
    alert(tt);    //undefined
    var tt = 'dd';
    alert(tt);    //dd
}

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/)

  以上代码片段虽然能够得到相同的结果,但最好不要那样写啦,习惯不好,代码不好维护。

  重申一下本文的重点:

  函数在定义它们的作用域里运行,而不是在执行它们的作用域里运行

  调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量)、函数参数及Arguments对象都在函数内的作用域中——这意味着它们隐藏了作用域链更上层的任何同名的属性。

来源:http://www.tulaoshi.com/n/20160219/1611449.html

延伸阅读
变量的作用域由声明它的位置决定。如果在过程中声明变量,则只有该过程中的代码可以访问或更改变量值,此时变量具有局部作用域并被称为过程级变量。如果在过程之外声明变量,则该变量可以被脚本中所有过程所识别,称为Script级变量,具有脚本级作用域。 变量存在的时间称为存活期。Script级变量的存活期从被声明的一刻起,直到脚本运行...
标签: 电脑入门
一、word域的含义、概念 WORD域的英文意思是范围,类似数据库中的字段,实际上,它就是Word文档中的一些字段。每个Word域都有一个唯一的名字,但有不同的取值。 首先,我们了解几个与域相关的概念。域是文档中的变量。域分为域代码和域结果。 域代码是由域特征字符、域类型、域指令和开关组成的字符串;域结果是域代码所代表的信息。域结果根...
魔兽争霸《神探狄仁杰》狄仁杰传奇高级难度第一节轻松过 首先要声明一下,我说的都是在最高难度下的。 首先选英雄,我比较喜欢狄仁杰,因为他的F技打怪比较轻松 进入游戏,有一个宝宝(关键就在这了),他有一个全屏闪的技能,我们可以用它到崇州的防具店那买东西,我选择的10木头的精甲,150防...
《心探》第十关怎么过?心探第十关通关流程攻略 心探第十关怎么过 第十关关卡介绍: 这一关是一个信封,在信封外面我们看不出一点破绽,那么怎么才能找到这一关的爱心呢?下面我们就来看一下心探第十关的闯关技巧吧! 第十关闯关技巧: 在这一关我们只需要按住信封,然...
概述 基本上每种语言都要讨论这个话题,C语言也不例外,因为只有你完全了解每个变量或函数存储方式、作用范围和销毁时间才可能正确的使用这门语言。今天将着重介绍C语言中变量作用范围、存储方式、生命周期、作用域和可访问性。 变量作用范围 存储方式 可访问性 变量作用范围 在C语言中变量从作用范围包括全局变量和局部变量。全局变...

经验教程

26

收藏

86
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部