再探Javascript词法作用域

简介:
复制代码
复制代码
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
}
复制代码
复制代码

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

 

重申一下本文的重点:

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

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

本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/archive/2012/11/24/2785667.html,如需转载请自行联系原作者

相关文章
|
1月前
|
JavaScript 前端开发
js变量的作用域、作用域链、数据类型和转换应用案例
【4月更文挑战第27天】JavaScript 中变量有全局和局部作用域,全局变量在所有地方可访问,局部变量只限其定义的代码块。作用域链允许变量在当前块未定义时向上搜索父级作用域。语言支持多种数据类型,如字符串、数字、布尔值,可通过 `typeof` 检查类型。转换数据类型用 `parseInt` 或 `parseFloat`,将字符串转为数值。
20 1
|
1月前
|
存储 JavaScript 前端开发
解释 JavaScript 中的作用域和作用域链的概念。
【4月更文挑战第4天】JavaScript作用域定义了变量和函数的可见范围,静态决定于编码时。每个函数作为对象拥有`scope`属性,关联运行期上下文集合。执行上下文在函数执行时创建,定义执行环境,每次调用函数都会生成独特上下文。作用域链是按层级组织的作用域集合,自内向外查找变量。变量查找遵循从当前执行上下文到全局上下文的顺序,若找不到则抛出异常。
23 6
|
1月前
|
JavaScript
作用域和作用域链(js的问题)
作用域和作用域链(js的问题)
10 0
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JS的执行上下文、词法作用域和闭包(中)
深入理解JS的执行上下文、词法作用域和闭包(中)
|
30天前
|
JavaScript 前端开发
JavaScript 闭包:让你更深入了解函数和作用域
JavaScript 闭包:让你更深入了解函数和作用域
|
4天前
|
JavaScript
Vue.js中使用作用域插槽实现自定义表格组件
Vue.js中使用作用域插槽实现自定义表格组件
|
4天前
|
JavaScript
Vue.js中的作用域插槽有什么特点和应用场景
Vue.js中的作用域插槽有什么特点和应用场景
|
29天前
|
JavaScript 前端开发
JavaScript 作用域详解:如何影响变量生命周期
JavaScript 作用域详解:如何影响变量生命周期
|
1月前
|
自然语言处理 JavaScript 前端开发
【JavaScript技术专栏】深入理解JavaScript作用域与闭包
【4月更文挑战第30天】了解JavaScript的关键在于掌握作用域和闭包。作用域决定变量和函数的可访问范围,分为全局(在`window`或`global`对象中)和局部(函数内部)。闭包则允许函数访问其创建时的作用域,即使在其他地方调用。它通过作用域链保存对外部变量的引用,常用于实现私有变量、模块化和柯里化。然而,不当使用闭包可能导致内存泄漏和性能下降。理解这些概念能提升代码质量,但也需谨慎处理潜在问题。
|
1月前
|
JavaScript 前端开发
JavaScript 作用域
JavaScript 作用域