面试(二)之作用域、作用域链

232 阅读4分钟

摘录于var与let区别-详解块级作用域与局部作用域
摘录于面试官:说说作用域和闭包吧
ES6之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。
ES6的到来,为我们提供了‘块级作用域’,可通过:
(1)let和const来体现;
(2)大括号里面声明;
(3)块级变量生命周期:从大括号开始 -> 到大括号结束。

作用域

变量与函数的可访问范围。

全局作用域

1.最外层函数和在最外层函数外面定义的变量拥有全局作用域,可以在页面任何地方被访问

var outVariable = "我是最外层变量"; //最外层变量
function outFun() { //最外层函数
    var inVariable = "内层变量";
    function innerFun() { //内层函数
        console.log(inVariable);
    }
    innerFun();
}
console.log(outVariable); //我是最外层变量
outFun(); //内层变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined

2.所有末定义直接赋值的变量自动声明为拥有全局作用域

function outFun2() {
    variable = "未定义直接赋值的变量";
    var inVariable2 = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(variable); //未定义直接赋值的变量
console.log(inVariable2); //inVariable2 is not defined

3.所有window对象的属性拥有全局作用域
window对象的内置属性都拥有全局作用域,例如 window.namewindow.locationwindow.top 等。

局部作用域

又称为函数作用域。在函数里面声明的变量, 只能在函数内部被访问。
上例中,变量 inVariable 和函数 innerFun 都只拥有局部作用域。

块级作用域

在大括号里面 且 使用let声明的变量。

        if(true){
            var a = 10;//全局变量
            let b = 20;//块级变量(块级作用域 :只在大括号内部起作用)
            console.log(a,b);//10,20
            
        };

        console.log(a);//10
        // console.log(b);//报错  b is not defined

var与let区别

        var a = 10;//全局变量
        let b = 20;//全局变量

        function fn() {
            console.log(a, b);//10,20  
        };

        fn();

        console.log(a, b);//10,20
        console.log(window.a);//10
        console.log(window.b);//undefined

(1)
var: 本质是给window对象添加属性,挂载到window
let: 存在一个独立的script作用域中,与window脱离关系。
(2)预解析规则不同
var: 显示变量提升。(在声明前可以访问变量,获取的是undefined)
js编译器在预解析阶段,会把变量的声明提升到当前作用域最顶端,赋值语句还是在原地
let: 不存在变量提升\暂时性死区。(在声明前可以访问变量,获取的是referenceError)
暂时性死区: 在代码块内,使用let命令声明变量之前,该变量都是不可用的。 (3) 不允许重复声明

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}
        function fn(){

            /* var预解析规则: 声明提升到作用域最顶端  var a;  只是声明提前 */
            console.log(a);//undefined
            
            /* 注意这个报错原因: 不是因为b没有声明,而是因为b不能在赋值前被访问 */
            console.log(b);//报错  'b' before initialization
            
            var a = 10;
            let b = 20;
            console.log(a,b);
        };

        fn();

作用域链

当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找...一直找到全局作用域。我们把这种作用域的嵌套机制,称为 作用域链。

const命令

(1)const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
(2)const声明一个只读的常量。一旦声明,常量的值就不能改变。
(3)const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。 常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。