作用域与变量提升,浏览器的底层运行机制

312 阅读4分钟

一、作用域

作用域是变量的可作用返回,变量只有在自己的作用域下才会生效。函数会产生作用域,在函数内定义的变量只能在函数内使用

1.作用域分类

  • 局部作用域:函数内定义的变量和形参的作用域就是局部作用域;这样的变量就是局部变量
  • 全局作用域:在函数外定义的变量的作用域就是全局作用域;变量就是全局变量
  • 块级作用域:在代码块中定义的变量作用是块级作用域;变量就是块级变量ES6才支持 注意:局部变量只能在函数内使用,全局哪都可以,函数的形参也是局部变量,范围是所在的函数,函数内不适用关键字声明变量会被当做全局变量,在严格模式下,不使用关键字声明就会报错!
//a、b 都是在全局下定义的,所以他们的作用域就是全局,n是在fn中定义的,它的作用域就是fn;
var a=3;
var b=6;
function fn(){
   var n=3;
   console.log(n)
};
console.log(a,b);

注意:在不同作用域下同名名变量不会冲突,隔离变量

var a = 10,
    b = 20;
function fn(x) {
    var a = 100,
        c = 300;
    console.log('fn()', a, b, c, x)
    function bar(x) {
        var a = 1000,
            d = 400
        console.log('bar()', a, b, c, d, x)
    }

    bar(100)
    bar(200)
}
fn(10)

全局变量和私有变量

  • 全局变量:在全局作用域定义的变量就是全局变量
  • 私有变量:形参,在函数私有作用域定义的变量

2.作用域链

1.作用域链是什么

函数会限制变量的作用域范围,而函数内是可以在嵌套函数的,函数层层嵌套,形成了一个作用域链

2.作用域链寻找变量的过程

当函数内使用某个变量的时候,会按照如下过程找到该变量:自身-上级-全局,如果其中找到之后就会停止查找,吐过没有就会报引用错误提示变量没有定义

3.注意:

一个变量的作用域只与函数声明的位置有关,与函数调用的位置无关。

function fn(){
   n=3; // 自己私有作用域中没有,向上级进行查找,上级作用域也没有,就相当于给window.n=3;
}
fn();
console.log(n);

二、浏览器底层运行机制

浏览器在执行之前会创建一个ECStack(Execution context Stack)执行环境栈和heep堆

    • ECStack(Execution context Stack) 执行环境栈(栈内存,从内存中分配出来的一块内存)
  • EC(Execution Context)执行上下文(在编程语言中,代码执行,为了区分全局作用域和函数执行所处不同的作用域,目的是为了区分每个词法作用域下代码的独立性)EC 就是代码执行所处的范围
  •  EC(g):全局执行上下文
    
    • EC(function):函数执行的上下文
  • VO 和AO:在每一个执行上下文中,代码执行的时候,都会存贮一些变量:
    • 全局上下文中的变量存储在VO
    • 私有上下文中的变量存储在AO
  • GO:浏览器把一些内置的属性和方法放到了GO中,并且在全局执行上下文(EC(g))中创建了一个window变量对象,并且让其指向GO

image.png

三、变量提升

当浏览器开辟出供js执行的栈内存之后,代码并不是立即自上而下执行,而是需要先做一些事情:把当前作用域中带var 和function 的关键字进行提前的声明和定义,这叫做变量提升机制。


var 和function 在变量提升阶段区别:

  • var 在变量提升阶段,只声明,未定义(不赋值)
  • function 在此阶段事声明和定义完成
var a=2;
var b=a;
b=3;
console.log(b)

以函数为分界线,函数以上的内容会给全局来一份

console.log(fn); //undefined
if(1=="1"){
   console.log(fn); //函数
   function fn(){};
   fn=3;
   console.log(fn);//函数
}
console.log(fn);//函数

1.只对等号左边的做变量提升

console.log(fn);//undefined
console.log(fn(1,2));//报错,因为nudefined不是函数
var fn=function (n,m){
    return n+m;
 }
console.log(fn(3,4));

2.return 下面的代码进行变量提升,return后面的代码不进行变量提升

function fn(){
   console.log(a);//undefined
   return function f1(){
   }
   var a=3;
}
fn();

3.如果变量名字重复

对于var的不会进行重复声明,但是会重新赋值在变量提升阶段,看到第一行var num,会声明一个变量num,此时看到第二行代码还有一个就不用在声明了

var num=2;
var num=3;
console.log(num);//3

对于function的变量提升阶段是声明和定义一起完成的,如果遇到重复声明定义的,会进行重复赋值

console.log(fn)//函数3
function fn(){
   console.log(1);
}
function fn(){
   console.log(2);
}
function fn(){
   console.log(3);
}

4.自执行函数在当前所在的作用域中不进行变量提升

自执行函数自己所形成私有作用域照常进行

function f2(){
   console.log("f2");
}
// 自执行函数在此处不进行变量提升
(function (){
   console.log(a);// undefined, 照常进行变量提升
   var a=3;
})();