JavaScript:作用域:函数作用域/全局变量污染/命名空间
在C#中我们都基本上不讲作用域,因为一切都是自然而然的(用语言描述反而有些困难)。但JavaScript的作用域,让人非常头大!
局部变量
如果一个变量在函数体内部(用var)声明,则该变量的作用域为整个函数体,在函数体外不可引用该变量。(另见:let)
不同函数内部的同名变量互相独立,互不影响。
这样被声明的变量被称之为:局部变量。
function scope() { var sname = ‘李志博‘; console.log(‘in function:‘ + sname); } scope(); console.log(‘out function:‘ + sname);
全局变量“污染”
不在任何函数内定义的变量具有全局作用域,被称之为全局变量。
演示:
如果script只是在HTML页面中使用,全局变量还可以接受(而且比较方便);
但随着JavaScript规模扩大,一个项目可能引用多个(第三方/其他人写的)类库(js文件),各个文件间的名称冲突就越来越难以避免,给开发/维护带来极大的问题!这就被称之为“全局变量污染”。
词法作用域
观察以下代码:
var sname = "飞哥"; function smart() { alert(`${sname}最帅`); } function reallySmart() { var sname = ‘子祥‘; smart(); } reallySmart();
reallySmart()调用smart(),smart()中需要sname但又没有声明sname,怎么办?
- 使用reallySmart()中定义的‘子祥‘,还是
- 全局变量”飞哥”
由var声明的变量的作用范围,其作用域由是由其源代码的书写位置,(而不是在哪里执行)决定的。
JavaScript的函数中不仅可以使用全局变量,还可以使用该函数外部的函数所声明的变量:
function outFunc(sname) { var age = 100; function innerFunc() { alert(age); //age定义在innerFunc()之外 } innerFunc(); }
这样就形成了一个作用域链:JavaScript会沿着这个链条由内向外查找,直到undefined。
@想一想@:如何剪切/粘贴第一个函数,才能显示:子祥最帅?
名称空间(namespace)
其他成熟的工程化语言内置了名称空间(namespace)来解决这个问题,比如:
//虽然都是“源栈”,但他们显然是不一样的: China.Chongqing.Luckystack China.Bejin.Luckystack US.NewYork.Luckstack
JavaScript只能模拟:
- 先定义一个唯一的全局变量(对象)
- 其他变量都写出是上述全局变量的成员(属性和方法)
- 还可以多层嵌套,最后形成名称空间一样的“样式”
var China = {}; China.Chongqing = {}; China.Chongqing.LuckyStack = {}; China.Chongqing.LuckyStack.wpz = function () {
JQuery等类库就是这样做的。(演示)
strict模式(ES5)
把我们之前的代码稍作改动:
function scope() { /*var*/ sname = ‘李志博‘; //注释掉var
@猜一猜@:会有什么结果?
如果在JavaScript的函数中声明变量,不使用var,该变量就具有全局作用域!——特性超级坑爹的一个“特性(bug)”,尤其是在代码review的时候,你根本不知道这是在:
- 使用一个已声明的变量,还是
- 要声明一个全局变量
所以从ES5开始,JavaScript就引入了所谓的“严格”模式,在代码顶部添加一行:
‘use strict‘; -- 如果浏览器不支持?
使用严格模式,就能强制JavaScript声明变量时必须使用:var;否则会报错。(以及其他约束)
演示:调试窗口报错
官方推荐总是使用严格模式。但是,如果
- a.js 文件上声明了‘use strict‘
-
b.js 文件没有声明‘use strict‘且没有按照严格模式书写代码
- 在html文件中先引用了a.js,然后再引用了 b.js
@想一想@:会出现什么情况?
所以,更多时候,我们不得不把‘use strict‘声明在函数顶部。
体会:JavaScript在处理大型项目,进行工程化开发方面先天不足!由此诞生了很多“奇巧淫技”,以及不断进化的ES标准。
但你以为这样就结束了?too young too simple啊!作为一个“先天严重不足,后天各种补丁”的语言,这一切才刚刚开始……
作业
-
使用“模拟名称空间”技术,构建一个函数函数yz.fei.get(number);
- yz.fei.get(number)除number以外,还可以接受任意多个回调函数做参数
- 这些回调函数能对number进行运算,并返回bool值的,比如has9()/has8()/has6()
- get()函数依次运行它的回调函数,只要回调函数运行结果为真,就累加计数
- 最后返回累加值
让yz.fei.get(number)调用has9()/has8()/has6(),实现之前“统计含9/8/6数字个数”的作业
JavaScript:作用域:函数作用域/全局变量污染/命名空间
原文地址:https://www.cnblogs.com/nntisn351/p/14174958.html