前言JavaScript有一个特性叫做作用域(Scope)。虽然对于很多开发新手来说,作用域的概念并不是很容易理解。在这篇文章中,我会尽量用最简单的方式来解释作用域和作用域链。希望你有所收获!作用域(Scope)1.什么是作用域作用域是变量、函数和对象在运行时代码的某些特定部分的可访问性。换句话说,范围决定了代码块中变量和其他资源的可见性。可能这两句话不好理解,先看一个例子:1functionoutFun2(){2varinVariable="Innervariable2";3}4outFun2();//必须先执行这个函数,否则不知道里面有什么什么5console.log(inVariable);//UncaughtReferenceError:inVariableisnotdefined从上面的例子就可以理解作用域的概念了。变量inVariable没有声明在全局范围内,所以在全局范围内取值会报错。我们可以这样理解:作用域是一个独立的站点,这样变量就不会泄露或暴露。也就是说作用域最大的用处就是隔离变量,不同作用域的同名变量不会发生冲突。在ES6之前,JavaScript没有块级作用域,只有全局作用域和函数作用域。ES6的到来为我们提供了“块级范围”,这可以通过添加命令let和const来体现。2.全局作用域和函数作用域代码中任何地方都可以访问的对象,都具有全局作用域。一般来说,以下几种情况具有全局作用域:最外层函数和定义在最外层函数外的变量具有全局作用域1varoutVariable="我是最外层变量";//最外层变量2functionoutFun(){//最外层函数3varinVariable="Innervariable";4functioninnerFun(){//内层函数5console.log(inVariable);6}7innerFun();8}9console.log(outVariable);//我是最外层变量10outFun();//内层变量11console.log(inVariable);//inVariableisnotdefined12innerFun();//innerFunisnotdefined所有未定义的直接赋值变量自动声明为具有全局作用域1functionoutFun2(){2variable="undefined直接赋值变量";3varinVariable2="内部变量2";4}5outFun2();//必须先执行这个函数,不然不知道里面是什么6console.log(variable);//undefined直接赋值变量7console.log(inVariable2);//inVariable2isnotdefinedwindow对象的所有属性有球alscope,window对象的内置属性具有全局作用域,如window.name、window.location、window.top等。全局作用域有一个缺点:如果我们写了很多行JS代码,函数中没有包含变量定义,那么它们都在全局作用域中。这样会污染全局命名空间,容易造成命名冲突。1//张三写的代码中的2vardata={a:100}34//李四写的代码中的5vardata={x:true}这就是jQuery、Zepto等库的源码为什么会放在在(函数(){....})()。因为放在里面的所有变量都不会泄露和暴露,不会污染外面,也不会影响其他库或JS脚本。这是功能范围的体现。函数作用域是指在函数内部声明的变量。与全局作用域相反,局部作用域通常只能在固定代码段内访问,最常见的是在函数内部。1functiondoSomething(){2varblogName="乘风破浪";3functioninnerSay(){4alert(blogName);5}6innerSay();7}8alert(blogName);//脚本错误9innerSay();//脚本错误范围是分层的,内部作用域可以访问外部作用域中的变量,但反之则不行。让我们看一个例子。使用气泡可能更容易理解作用域:最终输出结果为2、4、12。气泡1是全局作用域,标识符为foo;气泡2是作用域foo并具有标识符Symbolsa、bar、b;气泡3是范围栏,只有标识符c。值得注意的是,块语句(花括号“{}”之间的语句),例如if和switch条件语句或for和while循环语句,与函数不同,不会创建新的作用域。在块语句中定义的变量将保留在它们已经存在的范围内。1if(true){2//'if'条件语句块不会创建新的作用域3varname='Hammad';//name还在全局作用域中4}5console.log(name);//logs'初学者到Hammad'JS通常需要一段时间才能习惯变量提升,不理解这种特殊行为会导致错误。正因如此,ES6引入了块级作用域,让变量的生命周期更加可控。3、块级作用域块级作用域可以通过新的命令let和const来声明,声明的变量在指定块的作用域外是不能访问的。块级作用域在以下情况下创建:在函数内部在代码块内部(由一对花括号包裹)let声明的语法与var的语法相匹配。您基本上可以使用let而不是var进行变量声明,但它会将变量的范围限制在当前代码块内。块级作用域有以下特点:声明变量不会被提升到代码块的顶部let/const声明不会被提升到当前代码块的顶部,所以需要手动把let/const声明到顶部让变量在整个代码块中可用。1functiongetValue(condition){2if(condition){3letvalue="blue";4returnvalue;5}else{6//这里没有value7returnnull;8}9//这里没有value0}禁止重复声明if标识符已经在代码块内定义,然后在该代码块内对let声明使用相同的标识符将导致抛出错误。例如:1varcount=30;2letcount=40;//UncaughtSyntaxError:Identifier'count'hasalreadybeendeclared在这个例子中,count变量被声明了两次:一次用var,一次用let。因为let不能在同一个范围内重新声明一个已经存在的标识符,所以这里的let声明会抛出一个错误。但是如果你使用let在嵌套范围内声明一个同名的新变量,则不会抛出错误。1varcount=30;2//不会抛出错误3if(condition){4letcount=40;5//其他代码6}循环中绑定块作用域的妙用开发者可能最希望实现块级的效果for循环域,因为声明的counter变量可以限制在循环内,例如在JS中经常看到如下代码:1234我们要实现这样一个需求:点击一个按钮,提示“Thenthbuttonwasclicked”,这里先不考虑事件代理。我们万万没想到,当你点击任何一个按钮时,“第四个”会在后台弹出。这是因为i是一个全局变量。当点击事件执行时,此时i的值为3。如何修改,最简单的方法是声明i1for(leti=0;i
