闭包
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();