函数和变量的有效范围就是作用域 1、作用域的概念vara10;functionf1(){console。log(a);}f1();变量a在函数外定义,可以在函数内使用functionf2(){varb20;}console。log(b);变量b在函数内定义,在函数外无法访问,报错:bisnotdefined 这是因为js中存在作用域的概念。 作用域: 作用域就是指定一个变量或者一个函数的作用范围。 能在页面的任何位置都可以访问,称为全局作用域 只能在局部(函数内)访问,称为为局部作用域 在全局作用域下声明的变量,称为全局变量 在局部作用域下声明的变量,称为局部变量 上述代码中,a是全局变量,b是局部变量 ES5中只有函数才有作用域,所谓是局部作用域也可以叫函数作用域。 作用域的作用就是为了把数据进行保护,不让外部的数据对我们的数据进行污染 2、函数和变量声明的提升 在JS中存在一个很重要的特性,函数和变量声明的提升。怎么理解声明提升呢?我们先来看一道面试题: 以下打印什么?console。log(a);vara123;console。log(a);console。log(f);f();functionf(){console。log(函数声明提升);} 处的代码如果按照我们以前的理解,代码从上而下执行,那么在执行这行代码的时候,a还没有被声明,所以直接访问一个没有被声明的变量,程序应该报错。 但是结果却大出所料,这里得到的结果是undefined。 处的结果也和我们最初的认识是不一样的,结果为f对应的函数对象。 造成这个结果是因为变量和函数的作用域提升的原因,什么意思呢? JS是解释性语言,JS引擎对代码的处理分为两步:预解析处理:在代码执行之前,对代码做全局扫描,对代码中出现的变量和函数提前声明处理;上面代码预解析后会变成这样: vara;变量提前声明,但不初始化 函数提前声明 functionf(){ console。log(函数声明提升); } console。log(a);undefined a123; console。log(a);123 console。log(f);函数对象 f();函数声明提升调用执行:然后自上而下的执行代码 3、声明提升的规则 声明提升是将变量或者函数的声明提升到当前作用域的最顶端。在具体使用的过程中存在以下需要注意的细节。变量和变量同名,解析之后只存在一个当前变量的声明。console。log(a); vara123; console。log(a); vara456; console。log(a);解析之后:vara; console。log(a);undefined a123; console。log(a);123 a456; console。log(a);456函数和函数同名,后面的声明将前面的覆盖。f(); functionf(){ console。log(1); } f(); functionf(){ console。log(2); } f(); functionf(){ console。log(3); }解析之后:functionf(){ console。log(3); } f();3 f();3 f();3函数和变量同名,只有函数声明提升,忽略变量的声明。console。log(a); vara123; console。log(a); functiona(){} console。log(a); functiona(){} console。log(a);解析之后:functiona(){} console。log(a);函数a vara123;将前面的函数覆盖,a的值变为123 console。log(a);123 console。log(a);123 console。log(a);123如果是命名函数,则只将前面的变量声明提升,函数不动。console。log(fn1); functionfn1(){ } console。log(fn1); console。log(fn2); varfn2function(){ } console。log(fn2);解析之后:functionfn1(){ } varfn2; console。log(fn1);fn1函数 console。log(fn2);undefined fn2function(){ } console。log(fn2);fn2函数 其他问题varabc9;从右往左赋值 不适用var声明变量不管在哪里都是全局变量 functionfn(){ a10; } 4、作用域链和访问规则 在JavaScript里面,函数内部是可以包含另一个函数的functiona(){functionb(){}} 此时函数b就被函数a包含越来了,这样就形成了两层作用域。 如果有以下代码:三个同名变量放在三个作用域内varx10;console。log(x);functiona(){varx20;console。log(x);functionb(){varx30;console。log(x);}b();}a(); 会依次输出:10,20,30 虽然多个变量x同名,但是不同作用域内优先使用自己内部作用域的变量x。 如果代码做一下修改:删除函数b的局部变量xvarx10;console。log(x);functiona(){varx20;console。log(x);functionb(){console。log(x);}b();}a(); 依次输出:10,20,20 函数b内部没有变量b,会向自己的外面的作用域查找x变量,函数a内的x变量离函数b最近,会优先得到函数a的变量x 代码再做修改:再删除a的局部变量xvarx10;console。log(x);functiona(){函数a内部的变量x也没有了console。log(x);functionb(){函数b内部没有x变量了console。log(x);}b();}a(); 会依次输出:10,10,10 函数b内部没有x变量,会向函数a的作用域查找,但是函数a内部也没有x变量,会向函数a的上一层作用域再查找,直到查找到了全局作用域。 代码再次变化:全局的变量x也删除全局的变量x也没有了functiona(){函数a内部的变量x也没有了functionb(){函数b内部没有x变量了console。log(x);}b();}a(); 函数b内部没有变量x,会顺着上层作用域一层一层地查找,直到全局作用域也没有,就会报错。 总结:只有函数可以制造作用域链结构只要是代码,就至少有一个作用域,即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。将这样的所有的作用域列出来,可以有一个结构:函数内指向函数外的链式结构作用域链。由内而外的访问规律,一直向外找,找不到,报错。