JS:作用域以及作用域链

刚刚看了一篇曾在阿里工作的员工写的博客,不得不说,在生活中,总是会发现那些很优秀又有正能量的人,从他们的身上学到的不仅仅是知识,更是一种态度,我是一个新手,但是我选择热爱我所做的事情,基础很重要,学习前端的人甚多,但是我想在这条路上刨一刨,以求甚解,发现前端的美。

作用域:

作用域就是变量和函数可访问范围,控制着变量的函数可见性与生命周期。
执行环境:定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然在我们编写代码的时候无法访问它,但是解析器会在处理数据的时候,在后台使用它。

全局执行环境是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不同。在web浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window的属性和方法创建的。
某个执行环境中所有的代码执行完毕之后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或者浏览器时才会被销毁)

每个函数都有自己的执行环境。当函数流进入一个函数时,函数环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权交给之前执行的环境。

变量没有在函数内声明或者声明的时候没有带var 就是全局变量,拥有全局作用域,window对象的所有属性拥有全局作用域,在代码的任何地方都可以访问,在函数内部声明并且以var修饰的变量就是局部变量,只能在函数体内使用 ,函数参数虽然没有用var声明但依然是局部变量。
例如:

    var a = 3;//全局变量
        function fn(b){//b作为形参是一个局部变量
            c = 2;//全局变量
            var d = 5;//局部变量
            function subFn(){
                var e = d;//复函数的局部变量对子函数可见
                for(var i = 0;i<3;i++){
                     console.log(i);
                }
                console.log(i);//3,在for循环外可以访问到i的值和JS中无块级作用域有关,下文解释
            }
            subFn();

        }
        fn();
JavaScript中无块级作用域

JavaScript中无块级作用域,在其他类似C语言中,由花括号封闭的代码都有自己的作用域(如果用ECMAScript的话来讲,就是他们自己的执行环境),例如:

if(true){
  var color = "blue";//if语句中定义了color,如果是在C,C++或者Java中,color会在if语句执行完毕之后被销毁
}
console.log(color);//"blue"if语句中定义了color,如果是在C,C++或者Java中,color会在if语句执行完毕之后被销毁

for(var i = 0;i<10;i++){
  doSomething(i);
}
console.log(i);//10for语句创建的变量i即使在for循环执行结束完毕之后,也依然会存在循环外部的执行环境中

在if语句中定义了color,如果是在C,C++或者Java中,color会在if语句执行完毕之后被销毁。但是JavaScript,由for语句创建的变量i即使在for循环执行结束完毕之后,也依然会存在循环外部的执行环境中。

声明变量

使用var 声明的变量会自动添加到最近的执行环境中去。在函数内部,最接近的环境就是函数的局部环境

预解析

在真正执行代码之前,JavaScript解析器辉预解析代码,将变量,函数声明部分提前解释,这就意味着我们可以在function声明语句之前调用function
例如:

console.log(a);//undefined
   var a = 3;
   console.log(a);//3
   console.log(b);//Uncaught ReferenceError:b is not defined

上面的代码在执行var a = 3;语句之前其声明部分就得到了预解析(但是不会执行赋值语句),所以第一次的时候是undefined,而不会报错,在执行过赋值语句之后会得到3,所以上面这段代码得到的效果相当于下面这段代码:

var a;
console.log(a);
a = 3;
console.log(a);
console.log(b);

作用域链

当代码在一个环境中执行的时候,会创建变量对象的一个作用域。作用域链的用途是:保证对执行环境有权访问的所有变量 和函数的有序访问。作用域的最前端,始终是当前执行代码所在的环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含的(外部)环境,再下一个变量对象则来自于下一个包含环境。这样一直延续到全局执行环境,全局执行环境的变量对象始终都是作用域链的最后一个对象。
例如:

  var color = "blue";
       function changeColor(){
        var anotherColor = "red";
        function swapColors(){
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;
            //这里可以访问color,anotherColor和tempColor
        }
        //这里可以访问到color,anotherColor,但是不能访问到tempColor
        swapColors();
       }
       //这里只能访问color
       changeColor();

以上代码共涉及三个执行环境,全局环境,changeColor()的局部环境和swapColor()的局部环境。全局环境中只有一个变量color和一个函数changeColor()的函数,但是它也可以访问全局环境中的变量color,s 中有一个变量tempC lor,该变量只能在这个环境中访问到
内部环境可以通过作用域链访问到所有的外部环境中去,但是外部环境不能访问内部环境的任何变量和函数。
举例具体说明作用域链创建的过程:

function compare(value1,value2){
   if(value1<value2){
      return -1;
   }
   else if(value1>value2){
     return 1;
   }
   else{
     return 0;
   }
}

以上代码先定义了compare函数,然后又在全局作用域中调用了它。在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数[[Scope]]属性中的对象构建的起执行环境的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并推入执行环境作用域前端。对于这个例子compare()函数的执行环境而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不包含变量对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值