调用对象位于做用域链的前端,局部变量(在函数内部用var声明的变量)、函数参数及Arguments对象都在函数内的做用域中——这意味着它们隐藏了做用域链更上层的任何同名的属性。
2010年9月14日,我去参加网易网页工程师招聘会,应聘JS工程师职位。有幸参加笔试,而后有幸栽在笔试,呵呵。废话少说,抓出音响极深的一题从新研究研究。
题目大概是:写出以下代码的输出结果并进行分析前端
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 }