js作用域以及变量提升

怎甘沉沦 提交于 2020-02-26 16:27:19

一、作用域

 

定义

作用域,即产生作用的特定区域。

javaScript的作用域,即js的变量或者函数产生作用的对应区域。也就是说区域内的可以访问区域外的变量和函数,但是区域外的则不能访问区域内的变量和函数。

分类

在ES5中,作用域分为两种:全局变量和局部变量。

(1)全局变量:所有地方均可访问(在函数外部声明的变量);

(2)局部变量:只能在函数内部访问(在函数内部用var关键字声明的变量以及函数的形参)。

在ES6中,新增了块级作用域。

(3)块级作用域:只能在距离最近的大括号的作用范围内访问(仅限于let声明的变量)。

应用

// 全局变量
var i = 0 ;
// 定义外部函数
function outer(){
    // 访问全局变量
    console.log(i); // (1)
    function inner1(){
        console.log(i); // (2)
    }

    // inner2中定义局部变量
    function inner2(){
        console.log(i); // (3)
        var i = 1;
        console.log(i); // (4)
    }
    inner1();
    inner2();
    console.log(i); // (5)
}

outer();//输出结果依次为:0  0  undefined  1  0

注释(1)处:outer()内未声明和定义局部变量i, 故js解析器会继续在函数外部寻找i变量,最终在全局变量中找到i,因此结果为0;

注释(2)处:inner1()和outer()内均未声明和定义局部变量i,因此结果为0。

总结1:js解析器查找变量,依次从作用域内部一层一层往外查找,找到后结束,找到最外部都没有找到,结果就为undefined(已声明,但未定义)。

注释(3)处:结果是undefined,为什么?

             常见错误结果:0    inner2中变量i的声明与定义在此命令之后,找不到i变量,往函数外部查找,所以为全部变量i=0

             常见错误结果:1    inner2中变量i已经声明定义,所以函数内部已经找到i了,所以为1;

注释(4)处:因为inner2()内部重新定义了局部变量i=1,故结果为1。

注释(5)处:outer()内未声明和定义局部变量i,因此结果为0。

总结2:js中存在变量提升(hoisting),但仅仅是变量提升,定义并未提升。

 

二、变量提升(hoisting)

 

定义

变量声明是在任意代码执行前处理的,在代码区中任意地方声明变量和在最开始(即变量对应的作用域范围的初始位置)的地方申明是一样的。也就是说,看起来一个变量可以在申明之前被使用!这种行为就是所谓的“hoisting”,也就是变量提升,看起来就像变量的申明被自动移动到了函数或全局代码的最顶上。

说明:

声明变量在ES5中包括用以下关键字声明的对象:

(1)var

(2)function

声明变量在ES6中还包括用以下关键字声明的对象:

(3)function *

(4)let

(5)const

(6)class

应用

var temp = new Date();
f();
document.write("<br>"+"------------------分割线-------------------"+"<br><br>");
m();
document.write("<br>"+"---全局变量---temp="+temp+"<br>");

function f(){
    document.write("f    in!   0  temp="+temp+"<br>");
    if(false){
        var temp = "hello";
        document.write("f    in!   1  temp="+temp+"<br>");
    }
    document.write("f    in!   2  temp="+temp+"<br>");
}

function m(){
    document.write("m   in!   0  temp="+temp+"<br>");
    if(true){
        var temp = "hello";
        document.write("m   in!   1  temp="+temp+"<br>");
    }
    document.write("m   in!   2  temp="+temp+"<br>");
}

图一:

图二:

分析:

(1)图一在声明和定义之前就调用执行f()和m()函数,说明f()和m()的声明和定义分别进行了提升。

(2)f()和m()中分别定义了局部变量temp,由于在任何情况下,变量的声明都会提升到其作用域的顶部,因此f()和m()中的局部变量temp的声明会提升到各自函数的顶部,即图一等价于图二。

(3)由于改变的是每个函数中的局部变量temp的值,因此全局变量temp的值并不会被影响。

特殊情况的说明:

(1)变量重复声明

var a = 100;
alert(a);//100
var a = 200;
alert(a);//200

function fun2() {
	alert(a);//underfind;
	var a = 3;
    alert(a);//3
}
fun2();
alert(typeof a);//unmber

var a = function() {}
alert(typeof a);//function

等价于以下的代码

var a;
var a ;
var a ;//多次声明会合并为一个对象

a=100;//赋值操作
alert(a);
a=200;
alert(a);

function fun2() {
	var a;
	alert(a);//underfind;
	a = 3;//确定变量的数据类型,赋值
    alert(a);//3
}
fun2();
alert(typeof a);//unmber

a=function(){};
alert(typeof a);//function

分析:

js的var变量只有全局作用域和函数作用域(局部作用域)两种,且声明会被提升,因此实际上a只会在最顶上开始的地方申明一次,故var a=200的声明会被忽略,仅用于赋值。

(2)函数的提升:

函数提升分为两种情况:

      第一种:函数申明。如:function foo(){}这种形式(在提升的时候,会被整个提升上去,包括函数定义的部分)

      第二种:函数表达式。如:var foo=function(){}这种形式(其实是var变量的声明定义,故只提升声明,不提升定义)

(3)实例分析:

案例一:

案例二:

分析:

(1)通常通过var声明一个变量,在没有赋值的情况下只做声明,没有定义变量的数据类型。

(2)通过function声明一个变量,在声明的同时会定义变量的数据类型为funtion。

(3)在同级作用域内,相同名称的变量会合并为一个变量,也就是后者会覆盖前者。

综上所述:

function foo(){};--------定义一个foo变量,数据类型为function; 

var foo;-------------------定义一个foo变量,覆盖前者,然而没有定义变量类型。

因此注释(1)处foo的数据类型仍然是function。

当为foo变量重新赋值为500时,相当于重新定义了foo变量的类型

因此注释(2)处的foo数据类型为number。

三、总结

要彻底理解JS的作用域和变量提升(Hoisting),只要记住以下三点即可:

      1、所有声明都会被提升到作用域的最顶端,变量声明提升仅仅是声明提升,定义不提升;函数声明会连带定义一起被提升;

      2、同一个变量声明只进行一次,因此同一个变量的其他声明都会被忽略,从而变成赋值操作;

           如:var i = 0 ; var i = 1     相当于 var i ;  i = 0 ; i = 1;  

      3、函数声明的优先级优于变量声明,且函数声明会连带定义一起被提升。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!