闭包问题

闭包

function books() {
    var book = "书包里面的书本";
}
//  console.log(book);  //  ReferenceError: book is not defined
//  因为没有进入books函数进行执行上下文,所以无法访问到book变量
 
//  执行上下文(执行环境) = 全局环境 + 函数环境

function a() {
    b();
    function b() {
        c();
        function c() {
            console.log("i m here");
        }
    }
}
a();
//  首先进行  全局执行上下文(入栈)
//  如何进行  函数a执行上下文(入栈)
//  如何进行  函数b执行上下文(入栈)
//  如何进行  函数c执行上下文(入栈)
//  函数c内已经没有需要执行或者输出的内容
//  此时栈顶到栈尾依次为  函数c -> 函数b -> 函数a -> 全局
//  然后从栈顶向栈尾依次出栈直到回到全局执行上下文,代码才可以继续向下运行

//  执行上下文 = 创建阶段 + 执行阶段
//      创建阶段 = 作用域链 + 变量对象 + this
//          作用域链 = 当前变量对象 + 所有父级变量对象
//          变量对象 = 参数、变量、函数声明
//      执行阶段 = 变量赋值、函数引用

function books_2() {
    var book_2 = "书包里面的书本";
    return function() {
        console.log(book_2);
    }
}
var bag = books_2();
bag();
//  创建全局执行上下文 
//  全局执行上下文 = {作用域链:{全局变量对象}, {变量对象:books_2, bag}}
//  因为bag = books_2(); 所以进入函数books_2执行上下文
//      books_2执行上下文 = {作用域链:{books_2变量对象 + 全局变量对象}, {变量对象book_2}}
//  进入函数匿名函数执行上下文
//      匿名函数执行上下文 = {作用域链:{匿名函数变量对象 +books_2变量对象 + 全局变量对象}, {变量对象:}}
//  此时栈顶到栈尾依次为:匿名 -> books_2 -> 全局
//  出栈时首先运行匿名函数内的打印book_2,先查找匿名函数自己是否存在book_2变量,因为顶部会包括其以下的变量对象
//  所以自身没有时则查找父类变量对象,所以控制台会打印 “书包里面的书本”

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i++);
    },4000);
}
console.log('i=' + i);
//  创建全局执行上下文
//  setTimeout(0s也会)会创建一个任务队列,即在放入执行栈之前会先在任务队列内执行
//  当执行栈执行完毕后,才开始执行任务队列内的任务
//  所以setTimeout里面的匿名函数处理结果会先放到任务队列里面
//  即任务队列中有五个待输出的 i++
//  因此首先执行的是5次for循环,然后执行循环外的打印,输出为 i=5,此时代表执行栈空
//  栈空后开始执行任务队列内的任务,即每隔4秒打印一次,共打印五次
//  即输出 5 6 7 8 9(i++ 是先输出再自增,所以第一次打印仍为 5)
//  控制台结果: i=5 5 6 7 8 9

//  解决方法  增加立即执行函数,改变作用域链
//  立即执行函数 (function(x) {})();
for(var i = 0; i < 5; i++) {
    (function(x) {
        //  x为形参,代表实参 i 的值
        setTimeout(function() {
            console.log(x++);
        },4000);
    })(i);
}
console.log('i2=' + i);

//  创建全局执行上下文
//  因为setTimeout外包裹了一层立即执行函数,所以在执行全局执行上下文时,同时执行栈内进行立即执行函数执行上下文
//  立即执行函数执行上下文 = {作用域链:{立即执行函数变量对象 + 全局变量对象}, {变量对象:x}}
//  全局执行上下文 = {作用域链:{全局变量对象}, {变量对象:i}}
//  执行立即执行上下文,setTimeout创建一个任务队列,因为立即函数,所以会直接在任务队列内推入x(即i)的值 0
//  执行完毕,立即执行上下文出栈
//  执行第二次立即执行上下文,同理推入值1
//  最终任务队列内包含了 五次任务的输,即 0 1 2 3 4
//  全局执行上下文出栈,先执行循环外打印函数,输出 i2=5
//  执行栈内为空,输出任务队列内任务 0 1 2 3 4
//  控制台结果: i2=5 0 1 2 3 4

//在外部环境调用函数内部的函数
//定义一个变量名
var last;
function numberone(){
    var b=20;
    function numbertwo(){
        var c=30;
        console.log(c);
    }
    last=numbertwo;
    console.log(b);
}
//先调用外部函数
numberone();
//再调用内部函数
last();
posted @ 2022-03-24 01:38  伏月廿柒  阅读(30)  评论(0)    收藏  举报