js深入(三)作用域链与闭包
在之前我们根绝对象的原型说过了js的原型链,那么同样的js 万物皆对象,函数也同样存在这么一个链式的关系,就是函数的作用域链
作用域链
首先先来回顾一下之前讲到的原型链的寻找机制,就是实例会先从本身开始找,没有的话会一级一级的网上翻,直到顶端没有就会报一个undefined
同样的js的机制就是这样的,函数在执行的时候会先函数本身的上下文的变量对象中查找,没有的话,也会从这个函数被创建的时候的父级的执行上下文的变量对象中去找(词法环境),一直找到全局上下文的变量对象(比如客户端的window对象),这个多层的执行上下文的链式关系就是函数的作用域链
盗一张图

作用域被创建的时机
大家可以看到,我在控制台声明了一个函数,并且打印了他,这个a函数的里边有一个[[scope]]属性,
这是一个内部属性,当一个函数被创建的时候,会保存所有的父级的变量对象(词法环境)到这个里边,比如说上图中 就有一个global 属性展开后,往下找你会发现很多我们常见的属性和方法,比如alert等等
如图

函数作用域的生命周期
姑且叫他生命周期,我是这么理解的,当进入一个函数的上下文,经历了创建阶段之后,就会把函数的作用域链创建出来,直到销毁这个上下文,这个作用域链也是存在的
先来一个正经的例子
function a(){
var aaa = 'aaa';
return aaa;
}
checkscope();
这个函数的生命周期是这样的
首先函数被创建,先把函数的作用域链保存到函数的[[scope]]属性上边
a.[[scope]] = [
globalContext.VO//这个也就是我们上边图片里边看的golbal
];
globalContext 全局上下文 VO 这个之前没有介绍 是Variable object的简称,也就是之前经常提到的变量对象
还有一个AO ,这个AO指的是函数被激活的时候(被执行)得活动对象
- 创建完成之后,执行到a函数,创建了a函数得执行上下文,并压入执行栈里边
现在执行栈里边已经有了两个执行上下文一个globalContext还有一个aContext
- 到了a函数之后,首先会做一些列得准备工作,就是之前讲到得函数得arguments,this等等
首先第一步复制之前得[[scope]]属性,创建作用域链
aContext = {
Scope: a.[[scope]],
}
然后开始初始化活动变量 argments对象 形参,函数声明,变量声明等等
最后把把活动变量也塞到作用域链中去
以上,一个函数得准备工作就算是做完了,然后下一步就是函数得执行阶段
之前讲过,在之后阶段得时候函数会根据代码给之前得活动对象赋值,然后执行里边得代码,直到执行完毕
最后,函数执行完毕,函数得上下文被从上下文栈中弹出销毁
在弹出得最后时候,a函数得结构大概长成这个样子
aContext = {
AO: {
arguments: {
length: 0
},
},
Scope: [AO, [[Scope]]]
}
接下来我们在举一个不正经得例子,就是为了证明一下作用域链即使在函数被销毁后,也会存在这么一个事实
闭包
首先什么是闭包,闭包是指在一个函数内部能够访问不是函数得参数,也不是局部变量得函数,所以广义得讲我们用的所有得函数都是可算作是闭包,都能访问全局变量。。。
不过工作中不是这样子得,说正题,给上边得问题举个例子
var item = '1'
function a(){
var item = '2'
function b(){
return item
}
return b;
}
var foo = a();
foo();
试着猜想一下这段代码得执行过程
还是来一步一步得解释一下
首先不用多想,进入全局代码,创建全局执行上下文,推入执行栈,全局上下文得一系列初始化
然后创建a , 创建上下文,推入执行栈,一些列得初始化
在执行a得时候创建了b函数,这个时候,还记得上边之前说过得把,作用域链是在被创建得时候确定得
这个时候得b函数得作用域链应该是这个样子的
bContext = {
Scope: [AO, aContext.AO, globalContext.VO],
}
这个是重点,我们先把执行过程说完
在a函数执行完毕之后,a的上下文栈被弹出
然后在后边执行b函数,然后一样的套路,进上下文压入栈
进栈一些列的初始化
执行完毕b的上下文被弹出
上边已经把顺序说的很清楚了对吧, 执行过程是a进栈出栈,b进栈出栈,但是你打印这段代码的时候
会打印出一个2,就是因为虽然说a的上下文被销毁了,但是b的作用域链里边还是有a的活动对象的
,在b的上下文里边可以找到这个item
这也就是我们之前所说的闭包,也符合闭包的定义
创建他的函数的上下文被销毁,但是他依然存在
在代码中引用了不是自身的参数或者局部变量
最后放一个网上很常见的面试题
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
从作用域链的角度思考一下会打印出什么结果,为什么会打印出这个结果
以上是我对js的作用域链和闭包的一些认识,有不足之处,希望批评指正
js深入(三)作用域链与闭包的更多相关文章
- JS详细图解作用域链与闭包
JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...
- 前端高质量知识(四)-JS详细图解作用域链与闭包
攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法 ...
- js之作用域链到闭包
一.作用域 全局作用域和函数作用域(局部作用域). 一个变量的作用域就是源代码中定义这个变量的区域. 二.作用域链和闭包 全局变量只有一个(window,globel),全局环境下每一个函数都会形成一 ...
- 1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This
参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p ...
- 在chrome开发者工具中观察函数调用栈、作用域链与闭包
在chrome开发者工具中观察函数调用栈.作用域链与闭包 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量 ...
- 前端基础进阶(六):在chrome开发者工具中观察函数调用栈、作用域链与闭包
在前端开发中,有一个非常重要的技能,叫做断点调试. 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量对象, ...
- 聊一下JS中的作用域scope和闭包closure
聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...
- 个人理解的javascript作用域链与闭包
闭包引入的前提个人理解是为从外部读取局部变量,正常情况下,这是办不到的.简单的闭包举例如下: function f1(){ n=100; function f2(){ alert(n); } retu ...
- 【进阶2-2期】JavaScript深入之从作用域链理解闭包(转)
这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://github.com/yygmind/blog/issues/18 红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一 ...
随机推荐
- window7使用MinGW在命令行编译C/C++源程序(从零开始,设置PATH,LIBRARY_PATH,C_INCLUDE_PATH)
1.要想在window命令行下面编译C/C++源程序,对于初学者来说,需要在window环境下面配置好GCC和G++编译器,我使用的是MinGW,下载地址为:http://sourceforge.ne ...
- .net core响应缓存
按照官网资料操作无效,这里使用https://github.com/speige/AspNetCore.ResponseCaching.Extensions的扩展包 安装AspNetCore.Resp ...
- System.Windows.Documents.Run
希望采用不同的方案来书写文字,可以使用多个TextBlock,也可以使用一个TextBlock+多个Run <TextBlock FontSize="12" Margin=& ...
- WPF 创建无边框的圆角窗口
原文:WPF 创建无边框的圆角窗口 如题所述,在WPF中要创建一个没有边框且为圆角的窗体,有如下几步工作要进行: 第一步:去掉窗体默认样式的边框 首先将窗体的背景设为透明,将允许透明的属性设置为Tru ...
- Win8Metro(C#)数字图像处理--2.12Sobel边缘检测
原文:Win8Metro(C#)数字图像处理--2.12Sobel边缘检测 [函数名称] 图像Sobel边缘检测函数SobelEdgeProcess(WriteableBitmap src) [ ...
- SqlServer 无法为可更新的订阅设置发布服务器登录名 sp_link_publication
原文:SqlServer 无法为可更新的订阅设置发布服务器登录名 sp_link_publication 没有截图: 创建可更新订阅,正常创建了发布,在订阅端创建订阅,最后一步提示完成,却出现了警告: ...
- WPF 在一个dll创建一个Window(包含xaml),在另一个dll中再次继承 会出错
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e92390eb-bbfa-42fb-baa9-2286444c0dca/the-comp ...
- WPF使用WindowChrome实现自定义标题框功能
代码: <Window x:Class="WpfDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx ...
- WPF 窗体边框处理
一般做wpf窗口时都不会使用默认的标题栏等,会把他隐藏掉 此时设置以下属性 WindowStyle.AllowsTransparency.ResizeMode 中的两个或三个都能达到目的. 有一种场景 ...
- UWP入门(七)--SplitView详解与页面跳转
原文:UWP入门(七)--SplitView详解与页面跳转 官方文档,逼着自己用英文看,UWP开发离不开官方文档 1. SplitView 拆分视图控件 拆分视图控件具有一个可展开/可折叠的窗格和一个 ...