作用域和作用域链,闭包

1 作用域

什么是作用域:作用域分为全局作用域和函数作用域。全局作用域可以理解为是全局对象(window对象,GO对象),而函数作用域可以理解为是一个函数内部的对象(AO对象)。

全局作用域:所有在script标签内的语句都处在全局作用域中;
页面打开时创建GO对象,关闭时销毁;
定义在全局作用域中的变量,变量名是GO对象的属性名,变量值是属性值。
函数作用域:所有在函数体内部的语句的处在函数作用域中;
函数执行时创建AO对象,执行完毕销毁AO对象;
函数作用域中的变量是AO对象的属性名,变量的值是AO对象的属性值;
下一次使用函数时会创建新的AO对象。

1.1语法分析

Js解释引擎会先扫描所有的js代码,查看代码有没有低级的语法错误,如果存在语法错误,则整个程序就不会执行,如果没有语法错误,则进入预解析(编译)阶段

1.2 预编译

​ 全局对象GO
1,创建AO对象 ==> Activation Object(活动对象,作用域,其实叫执行期上下文)

2,找到形参和变量,把形参和变量作为AO对象的属性名,值是undefined

3,实参把值赋给形参

4,在函数中找到函数声明,把函数名作为AO对象的属性名,值是函数体

       function myTest(a, b) {
            /*
            第一步,AO{ }
            第二步
            AO{
                a:undefined;
                b:undefined;
                c:undefined;
            }
            第三步
            AO{
                a:10;
                b:20;
                c:undefined;
            }
            第四步

            AO{
                a:function() {
                    console.log(100)
                };
                b:20;
                c:undefined;
            }
            
            */
            console.log('a1', a); //f a(){}
            console.log('b1', b); //20
            console.log('c1', c); //undefined
            var c = 30;
            b = 200;

            function a() {
 
            };
            console.log('a2', a); //f a(){}
            console.log('b2', b); //200
            console.log('c2', c); //30
        };
        let result = myTest(10, 20);
        console.log(result);

预编译后就开始一行一行执行代码,该改值的改值。要注意的一点是,这里的c是用var声明的,用let和const声明不会提升,会报错。

2 作用域链

1. 变量查询规则

当遇见使用一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候停止。(有同名的变量会发生“遮蔽效应”)

   let a = 1000;

        function myTest() {
            let b = 20;
            outer()
            function outer() {
                let c = 30;
                b = 200;
                console.log(a);//1000
                console.log(b);//200
                inner()
                function inner(){
                    a = 1;
                    console.log(a);//1
                    console.log(c);//30

                }
            }
        }
        let result = myTest();
        console.log(result);

2. 作用域链原理理解:

scope翻译过来就是范围的意思。
[[scope]]中所存储的就是执行期上下文对象的集合,这个几个呈链式连接,我们把这种链式链接叫做作用域链。
比方说上述代码中
myTest


image.png

outer


image.png

inner


image.png

作用域链 = 函数执行时的AO对象 + 函数创建时的环境。
变量查找规则:沿着当前函数作用域链作用域链顶端,自上而下寻找变量。

3 闭包

闭包是什么:闭包是指那些引用另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。把一个函数从它定义的那个作用域,挪走,运行。这个函数能够记忆住定义时的那个作用域。不管函数走到哪里,定义时的作用域就带到了哪里。这就是闭包。

上代码

 function myTest() {
        let count = 0;
        return function inner() {
            console.log(count)
        }
    }
    let  count = 10;
    let inn = myTest();
    let result =  inn()//0

我们都知道,inner是定义在myTest内,无法通过inner()直接在全局作用域中直接调用的;
但这里把inner函数 return出来,执行myTest等于返回一个inner函数。

  function sum(x,y) {
        return function innerSum(x) {
            return x + y
        }
    }
    let test = sum(3,7);
    test(1)//8,3被8代替了

每次调用一个函数,都会产生新的闭包.新的闭包知道是,语句全新,所处环境也是全新的。

        function myTest() {
            let count = 0;
            return function inner() {
                count++;
                return count
            }
        }
        let count = 10;
        let inn1 = myTest();
        let inn2 = myTest();
        inn1();//1
        inn1();//2
        inn1();//3
        inn2();//1

闭包的作用

1,函数累加器

function outer(){
    var count = 0;
    function inner(){
        count++;
        console.log(count);
    }
    return inner;
}

var inn1 = outer();

inn1(); //1
inn1(); //2 

2,可做缓存

function test(){
    var num = 100;
    function aa(){
        num ++;
        console.log(num);
    }
    function bb(){
        num --;
        console.log(num)
    }
    return [aa,bb]
}
var myArr = test()
myArr[0]()//执行num++,101
myArr[1]() //执行num--,100

你可能感兴趣的:(作用域和作用域链,闭包)