闭包与作用域链

176 阅读4分钟

最近一直在梳理自己的js,有些已经有点模糊了,梳理知识的同时深入研究了下。

变量作用域

变量作用域的概念:就是一个变量可以使用的范围

JS中首先有一个最外层的作用域:称之为全局作用域. JS中还可以通过函数创建出一个独立的作用域,其中函数可以嵌套,所以作用域也可以嵌套

`var age=18;     //age是在全局作用域中声明的变量:全局变量function f1(){
    console.log(name);      //可以访问到name变量
    var name="周董" //name是f1函数内部声明的变量,所以name变量的作用域就是在f1函数内部
​
    console.log(name);      //可以访问到name变量
​
    console.log(age);       //age是全局作用域中声明的,所以age也可以访问
}
​
console.log(age);       //也可以访问
    //多级作用域
    //-->1级作用域
    var gender="男";
    function fn(){
        //问题:
        //gender:可以访问
        //age:  可以访问
        //height:不能访问//-->2级作用域
        return function(){
            //问题:
            //gender:   通过一级一级作用域的查找,发现gender是全局作用域中声明的变量
            //age:
            //height:
            console.log(gender);
​
            //-->3级作用域
            var height=180;
        }
        var age=5;
    }`

作用域链

由于作用域是相对于变量而言的,而如果存在多级作用域,这个变量又来自于哪里?这个问题就需要好好地探究一下了,我们把这个变量的查找过程称之为变量的作用域链。

作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问) 简单来说,作用域链可以用以下几句话来概括:(或者说:确定一个变量来自于哪个作用域)。查看当前作用域,如果当前作用域声明了这个变量,就确定结果。查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明。再查找上级函数的上级函数,直到全局作用域为止。如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)

举例1:

var name="张三";
function f1(){
    var name="abc";
    console.log(name);
}
f1();

举例2:

var name="张三";
function f1(){
    console.log(name);
    var name="abc";
}
f1();

举例3:

var name="张三";
function f1(){
    console.log(name);
    var name="abc";
}
f1();

举例4:

var name="张三";
function f1(){
    return function(){
        console.log(name);
    }
    var name="abc";
}
var fn=f1();
fn();

举例5:

var name="张三";
function f1(){
    return {
        say:function(){
            console.log(name);
            var name="abc";
        }
    }
}
var fn=f1();

闭包的问题

`function fn(){

 var a=5;

 return function(){

 a++;

 console.log(a); //a变量肯定是可以访问的

 }

 }

 var f1=fn(); //f1指向匿名函数

 f1(); //6

 f1(); //7

 f1(); //8`
//代码执行到20行fn函数执行完毕,返回匿名函数

//一般认为函数执行完毕,变量就会释放,但是此时由于js引擎发现匿名函数要使用a变量,所以a变量并不能得到释放,而是把a变量放在匿名函数可以访问到的地方去了

//a变量存在于f1函数可以访问到的地方,当然此时a变量只能被f1函数访问

可以看出每次调用a的值+1 下面几个小例子:

例1:

function fn(){

 var a=5;

 return function(){

 a++;

 console.log(a); //a变量肯定是可以访问的

 }

 }

 var f1=fn(); //f1指向匿名函数

 f1(); //6

 f1(); //7

 f1(); //8

 //把a变量的值放在f1函数可以访问到的地方
var f2=fn();

f2();       //6

f2();       //7

f2();       //8

//又一次执行了fn,又初始化了一个新的a变量,值为5;返回匿名函数f2,并且把新的a变量放在了f2可以访问到的地方


var f3=fn();
f3();       //6

//又一次执行了fn,又初始化了一个新的a变量,值为5;返回匿名函数f2,并且把新的a变量放在了f2可以访问到的地方

例2:

function q1(){

    var a={};

    return a;

}

var r1=q1();

var r2=q1();

console.log(r1==r2);





function q2(){

    var a={}

    return function(){



        return a;

    }

}

var t3=q2();//创建一个新的a对象,把a对象放在t3可以访问到的位置
var o5=t3();    //返回值a就是那个a
var w3=q2();//创建了一个新的a对象,把新的a对象放在w3可以访问到的位置
var o8=w3();//此时获取到的是一个新的a对象
console.log(o5==o8);    //false

闭包问题的产生原因:函数执行完毕后,作用域中保留了最新的a变量的值 闭包的应用场景:模块化,防止变量被破坏

这里我又写了个闭包应用的小案例:

 var ktv=(function KTV(){

 //为了保护leastPrice变量,将它放在函数内部

 var leastPrice=1000;
        var total=0;
        return {
            //购物
            buy:function(price){
                total+=price;
            },

            //结账

            pay:function(){

                if(total<leastPrice){
                    console.log('请继续购物');
                }else{
                    console.log('欢迎下次光临');
                }

            },
            editLeast:function(id,price){
                if(id===888){
                    leastPrice=price;
                    console.log("现在最低消费金额为:",leastPrice);
                }else{
                    console.log('权限不足');
                }
            }
        }
    })()


    //有问题:来了一个工商局的朋友要来唱K

    //——>可能老板需要去修改最低消费的金额

    //-->但是并不能让老板直接去修改leastPrice,或者说不能把leastPrice作为全局变量

    var datepicker=(function(){
        var hour=3600*1000;
        return function(){
            console.log('日期控件初始化');
        }
    })();


    var common=(function(){
        return {
            isStr:function(){
            },
            isNumber:function(){
            }
        }
    })()

最后在我们或许会想到释放闭包内如何操作?

function f1(){

 var a=5;

 return function(){

 a++;

 console.log(a);

 }

 }

 var q1=f1();

 

 //要想释放q1里面保存的a,只能通过释放q1

 q1=null; //q1=undefined