-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
说一说你对JS上下文栈和作用域链的理解? #14
Comments
上下文栈: |
var a = 1; 首先浏览器进行编译过程,创建了a变量值为未定义,创建了函数b,当执行到第一句时,给a赋值为1;当执行到b(),在b的函数里面创建了c,并定义了c的值为2,当执行到下一句时,在函数b中,并没有定义a,他会往上一级一级一级的向上寻找知道找到a为止,如果没有则会报错,这样一级一级的往上查询就形成了 作用域链; |
/* /* let n = 1;
} fn(); |
上下文环境是在我们调用函数的时候产生的,而不是定义函数的时候产生的。话不多说,举个栗子:
根据上面这段代码来分析上下文栈的问题。 关于作用域链,首先要全局变量和局部变量的概念。全局变量是对于全局作用域来说的,局部变量是对于函数作用域来说的。在函数中引用一个变量的时候会先在当前作用域下寻找,如果没有找到就会去上层作用域中寻找,这样逐层找变量的过程,就会形成一条当前作用域下对该变量的一个寻值的链条,而产生的作用域链。 但是在局部变量中有一种巧妙的设计可以让当前局部变量活下来,我们给了他一个神奇的名字 —— 闭包。闭包在百度中的解释是:能够读取其他函数内部变量的函数。看起来不太好理解。话不多说,举个栗子:
一个很简答的函数自执行,这里面就用到了闭包。闭包一般都有一个特征就是在函数的内部定义一个函数,并且将这个函数作为返回值进行返回。那么此时我们得到的变量bar,其实就是该函数的返回值。这样的设计可以将局部变量a存储在内存中,享受跟全局变量一样的待遇。不同的是:他只能被返回值函数所调用,除了该函数之外他不会对其他看上去很靓很帅的函数多瞧一眼。所以说这个存储在内存中的局部变量也是蛮忠贞不渝的。 |
|
执行上下文栈(Execution Context Stack) 在ECMASscript中的上下文有三种类型:global, function和eval。 每一种代码的执行都需要依赖自身的上下文。当然global的上下文可能涵盖了很多的function和eval的实例。函数的每一次调用,都会进入函数执行中的上下文,并且来计算函数中变量等的值。eval函数的每一次执行,也会进入eval执行中的上下文,判断应该从何处获取变量的值。 注意,一个function可能产生无限的上下文环境,因为一个函数的调用(甚至递归)都产生了一个新的上下文环境。 function foo(bar) {}
// 调用相同的function,每次都会产生3个不同的上下文
//(包含不同的状态,例如参数bar的值)
foo(10);
foo(20);
foo(30); 一个执行上下文可以激活另一个上下文,就好比一个函数调用了另一个函数(或者全局的上下文调用了一个全局函数),然后一层一层调用下去。逻辑上来说,这种实现方式是栈,我们可以称之为上下文堆栈。 激活其它上下文的某个上下文被称为 当一个 caller 激活了一个 callee,那么这个 caller 就会暂停它自身的执行,然后将控制权交给这个 callee . 于是这个 callee 被放入堆栈,称为进行中的上下文 [running/active execution context] . 当这个 callee 的上下文结束之后,会把控制权再次交给它的 caller,然后caller会在刚才暂停的地方继续执行。在这个caller结束之后,会继续触发其他的上下文。一个 callee 可以用返回(return)或者抛出异常(exception)来结束自身的上下文。 如下图,所有的 ECMAScript 的程序执行都可以看做是一个执行上下文堆栈 [execution context (EC) stack]。堆栈的顶部就是处于激活状态的上下文。 当一段程序开始时,会先进入全局执行上下文环境[global execution context], 这个也是堆栈中最底部的元素。此全局程序会开始初始化,初始化生成必要的对象[objects]和函数[functions]. 在此全局上下文执行的过程中,它可能会激活一些方法(当然是已经初始化过的),然后进入他们的上下文环境,然后将新的元素压入堆栈。在这些初始化都结束之后,这个系统会等待一些事件(例如用户的鼠标点击等),会触发一些方法,然后进入一个新的上下文环境。 见下图,有一个函数上下文“EC1″和一个全局上下文“Global EC”,下图展现了从“Global EC”进入和退出“EC1″时栈的变化: ECMAScript运行时系统就是这样管理代码的执行。 关于ECMAScript执行上下文栈的内容请查阅本系列教程的第11章执行上下文(Execution context)。 如上所述,栈中每一个执行上下文可以表示为一个对象。让我们看看上下文对象的结构以及执行其代码所需的 状态(state) 。 作用域链(Scope Chains)
作用域链的原理和原型链很类似,如果这个变量在自己的作用域中没有,那么它会寻找父级的,直到最顶层。 标示符[Identifiers]可以理解为变量名称、函数声明和普通参数。例如,当一个函数在自身函数体内需要引用一个变量,但是这个变量并没有在函数内部声明(或者也不是某个参数名),那么这个变量就可以称为自由变量[free variable]。那么我们搜寻这些自由变量就需要用到作用域链。 在一般情况下,一个作用域链包括父级变量对象(variable object)(作用域链的顶部)、函数自身变量VO和活动对象(activation object)。不过,有些情况下也会包含其它的对象,例如在执行期间,动态加入作用域链中的—例如with或者catch语句。[译注:with-objects指的是with语句,产生的临时作用域对象;catch-clauses指的是catch从句,如catch(e),这会产生异常对象,导致作用域变更]。 当查找标识符的时候,会从作用域链的活动对象部分开始查找,然后(如果标识符没有在活动对象中找到)查找作用域链的顶部,循环往复,就像作用域链那样。 var x = 10;
(function foo() {
var y = 20;
(function bar() {
var z = 30;
// "x"和"y"是自由变量
// 会在作用域链的下一个对象中找到(函数”bar”的互动对象之后)
console.log(x + y + z);
})();
})(); 我们假设作用域链的对象联动是通过一个叫做__parent__的属性,它是指向作用域链的下一个对象。这可以在Rhino Code中测试一下这种流程,这种技术也确实在ES5环境中实现了(有一个称为outer链接).当然也可以用一个简单的数据来模拟这个模型。使用__parent__的概念,我们可以把上面的代码演示成如下的情况。(因此,父级变量是被存在函数的[[Scope]]属性中的)。 以上内容来自文章: https://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html 水平不够,总结不出来好的描述,等一个简单易懂的理解。 |
什么是执行上下文? 2.作用域链
|
执行上下文总共分为三个类型:
执行上下文栈函数多了,就有很多个函数执行上下文,每次调用函数创建一个新的执行上下文,而 执行流程:
作用域链查找一个变量,首先在它当前的所在的函数作用域查找,如果没有,就一层一层向上找,直到找到全局作用域还没有找到,就宣布放弃。这种一层层的关系,就是 作用域 |
上下文栈上下文栈是存放执行环境的。执行环境定义了变量和函数能够访问的其他数据(存储在这个执行环境的变量对象上),主要分为全局执行环境和函数执行环境。当执行到一个函数的时候就会将这个函数的执行环境压入这个执行栈,当函数执行完成,将就这个这个执行环境出栈,回到上层的执行环境。
作用域链每个执行环境都会有一个变量对象,用来存储这个环境中声明的变量或者函数。在上下文栈中执行的各个环境之间的变量对象之间会创建一个关系(作用域链)。在当前执行环境中的代码可以访问当前这个变量对象或者变量对象向上关联的变量对象直到全局变量对象上存储的变量和函数。 简而言之:作用域是变量查找的规则,作用域链定义了变量可以查找的范围。变量查找只能从当前作用域开始向上查找而不能向下, 并且变量的查找具有遮蔽效应,在作用域链上进行向上查找的时候,最先找到的同名变量会遮蔽更上层作用域链中的同名变量. 附上一张我根据自己理解画的一张图 |
1.执行上下文栈(Execution Context Stack(ECS))是执行JS代码时创建的执行栈结构。 全局执行上下文默认在执行上下文栈的最里面,当JS引擎发现一个函数调用,则创建这个函数的函数执行上下文,并把这个上下文push进栈,JS引擎执行栈顶上下文关联的函数,一旦函数执行完毕,则将其pop出栈,并往下执行。 我们再分析一下 函数执行上下文的两个阶段:创建阶段和执行阶段
执行阶段 作用域链
当cFunc被调用时,cFunc的作用域链是
当访问dFunc中的变量f时,JS引擎首先查看dFunc如果存在就打印。访问c变量时,js引擎首先在dFunc的可变对象中获取,不能获取,就到cFunc的可变对象中获取,找到就打印出来 文章引用: |
JavaScript执行上下文是JavaScript代码执行时的一个虚拟的环境,JavaScript首先会创建一个全局的执行上下文,如果后面遇到函数执行就创建该函数的执行上下文,并压入执行栈栈顶,当函数执行完成之后就从栈顶移除。JavaScript采用的是静态作用域,如果在访问某个变量的时候在自己的作用域中没有查询到,就会顺着作用域链向上查找知道全局作用域。 |
JavaScript是一门单线程的语言,所以它的执行顺行是由上往下执行的; 怎么执行效果不一样? JavaScript的执行顺序由上往下执行的没错,但是javascript不是一行一行的分析和执行代码,而是一段一段的分析执行,并且在代码执行前会有一个 ‘准备工作’,变量提升和函数提升就是属于代码的准备工作! 而这里的’准备工作‘就是 ------> 执行上下文(execution context). 当函数写的多了,怎么来管理这么多的‘准备工作呢’? JavaScript创建了执行上下文栈 ( Execution context stack, ECS )来管理执行上下文。 我们用数组来模拟一下 执行上下文栈 的行为: JavaScript运行时最先遇到的就是全局代码,所以初始化的时候会向 执行上下文栈 压入一个全局执行上下文,我们可以用globalContext来表示,当且仅当应用程序关闭的时候全局上下文才会消失。所以在程序关闭之前,ECStack最底下永远都有一个全局上下文 globalContext;
文章借鉴冴羽大神:mqyqingfeng/Blog#4 作用域链结合这两篇文章: |
(一)执行上下文 (二)执行上下文栈 (三)作用域 fun1(a) |
一、JS上下文栈(Execution context stack,ECS)理解执行上下文和栈可以让您了解为什么代码运行的结果和你最初预期的不同的原因。 可执行代码可执行代码包括三种,全局代码、函数代码、eval代码。当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做"执行上下文(execution context)"。
执行上下文栈关于执行栈,有5点需要记住: 二、作用域链对于每个执行上下文,都有三个重要属性:
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。 三、参考文章【冴羽:JavaScript深入之执行上下文栈】(mqyqingfeng/Blog#4) |
上下文栈:当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。 |
每一次代码执行和函数调用都会产生一个执行环境,称为执行上下文(context stack)。 作用域(scope chain)是每一个执行上下文自身持有的活动对象的集合,如在本执行上下文中声明的变量和函数以及方法参数传入的对象。 文章引用: JS 执行上下文栈 / 作用域链 |
js上下文包括全局执行环境,函数执行环境。 1.函数 swapName()的作用域链包含三个对象:自己的变量对象----->changeName()局部环境的变量对象 ----->全局环境的变量对象。 2.函数changeName()的作用域包含两个对象:自己的变量对象----->全局环境的变量对象。 就上述程序中出现的变量和函数来讲(不考虑隐形变量): 1.swapName() 局部环境的变量对象中存放变量 tempName; 2.changeName() 局部环境的变量对象中存放变量 name2 和 函数swapName(); 3.全局环境的变量对象中存放变量 name1 、函数changeName(); |
执行上下文 & 栈执行上下文主要分为全局上下文、函数上下文和eval上下文。 作用域链每一个作用域与当前上下文有关,它包括了当前上下文的定义的变量,函数,以及传入的参数。 |
**** 1. js的上下文栈 |
运行机制执行上下文每段js 都有一个执行环环境 也就是常说的执行上下文 执行上下文 又分文 全局执行上下文 和 函数执行上下文全局上下文 也就是最外层的执行环境 代码一开始就会先进入 执行的函数上下文 当代码执行到一个函数体的时候 就进入了函数执行的上下文执行上下文记录了代码运行时的环境,当前运行状态下 有且只有一个执行上下文起作用,问题执行上下文记录了什么了什么那? 也就是我们所说的词法环境 变量环境 执行栈(调用栈)代码运行时 首先会进入全局上下文 然后会执行到foot()时 就进入了 foo的上下文 ,然后代码继续执行 执行到a() 此时进入了 a的上下文 执行完a()后 又回到了 foo 上下文 执行文foo() 后 又回到了 全局上下文, 作用域链作用域是一个函数在执行时期的执行环境,每个执行环境都会有一个变量对象,用来存储这个环境中声明的变量或者函数 在各个执行环境之间的这个对象会行程查找关系 也就是所说的作用域链 |
JS上下文栈:一个javascript程序中,必定会产生多个执行上下文,javascript引擎会以栈的方式来处理它们,也就是执行上下文栈 |
上下是函数执行时产生的,es5中只有函数作用域,并没有块作用域,es6中let,const让变量有了块作用域 |
执行上下文栈是由Javascript引擎所创建来管理执行上下文(每个执行文都包含三个重要属性,变量对象Ov,作用域链, this) 比如有以下脚本
每执行一个函数,都会创建一个上下文,当执行以上代码时,执行栈事先会先把全局执行上下文压入栈中,再把函数c的执行上下文push进去,再push b的上下文,再push a的上下文。 作用域链当查找变量的时候,会从当前上下文的变量对象中查找,如果没找到会继续往父级的作用域查找,直到全局上下文。 函数内部有一个[[scope]] 属性,气主要功能是保存期父变量对象到其中
|
https://juejin.im/post/58eaecdea0bb9f0069271861 |
JS是一门单线程语言,从上往下执行的。但是代码并不是一句一句的往下面执行,而是一段一段代码分析执行,首先会经历编译阶段,接着才是执行阶段。 执行上下文执行上下文总共有三种类型
执行上下文栈因为JS引擎创建了很多的执行上下文,所以JS引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文。 当 JavaScript 初始化的时候会向执行上下文栈压入一个全局执行上下文,我们用 globalContext 表示它,并且只有当整个应用程序结束的时候,执行栈才会被清空,所以程序结束之前, 执行栈最底部永远有个 globalContext。 作用域链当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。 |
执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。
在一个JavaScript程序中,必定会产生多个执行上下文,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。 作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。 |
执行上下文栈 简单的来说就是当我们在执行函数时,js引擎会为我们创建一个执行的上下文, 执行函数(入栈),函数结束(出栈),这个出栈入栈的过程就叫 作用域链 作用域链就是自由变量(当前没有定义的变量,要往上级找)一层一层往上找,直到找到全局变量为止
|
执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。
作用域作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。—— 摘录自《你不知道的JavaScript》(上卷) 作用域有两种工作模型:词法作用域和动态作用域,JS采用的是词法作用域工作模型,词法作用域意味着作用域是由书写代码时变量和函数声明的位置决定的。(
不要再说ES6之前没有块级作用域了!了解下with(不被推荐)和catch~ try {
throw 2;
}catch(a) {
console.log(a); //2
}
console.log(a); //ReferenceError: a is not defined JS执行上下文栈(后面简称执行栈)执行栈,也叫做调用栈,具有 LIFO (后进先出) 结构,用于存储在代码执行期间创建的所有执行上下文。
以一段代码具体说明: function fun3() {
console.log('fun3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
作用域链作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。 如: var a = 10;
function fn1() {
var b = 20;
function fn2() {
a = 20
}
return fn2;
}
fn1()(); |
执行上下文
作用域 function foo(){
console.log(a);
}
function bar(){
var a = 20;
foo();
}
var a = 10;
bar();
// => 10 在这里,如果 |
1.什么是执行上下文?
2.执行上下文的类型
3.作用域链函数与函数之间可以进行嵌套的,函数作用域的嵌套就组成了所谓的函数作用域链。当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内仍找不到该变量,则会抛出异常。 |
JS引擎是浏览器的重要组成部分,主要用于读取并执行js。就是这家伙执行js的,但它不止于执行js。
//执行栈 这块还有个知识点:大家都知道闭包函数内存不会释放,那为什么内存不释放呢? 思考?????? 说了这么半天,那什么是执行上下文? 变量对象(VO): 浏览器在对代码进行解析时,会先进行变量声明和函数声明以及函数形参声明;
//活动对象
作用域链: 作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。 this: 不在赘述 具体看 https://github.com/YvetteLau/Step-By-Step/issues/1 通过查阅资料总结学习到就这么多,有不对的地方请多多包涵。。。 |
javascript运行的代码环境有三种:
作用域链:函数在执行时,每遇到一个变量,都会去执行上下文的作用域链的顶部,也就是执行函数的激活对象开始搜索,如果在第一个作用域链(即,Activation Object 激活对象)中找到了,那么就返回这个变量。如果没有找到,那么继续向下查找,直到找到为止。如果在整个执行期上下文中都没有找到这个变量,在这种情况下,该变量被认为是未定义的。 |
执行栈上下文包含三种环境: |
上下文栈js代码执行有三种环境,对应着三种上下文:
作用域
作用域有两种工作模型:词法作用域和动态作用域,JS采用的是词法作用域工作模型,词法作用域意味着作用域是由书写代码时变量和函数声明的位置决定的。(with 和 eval 能够修改词法作用域,但是不推荐使用,对此不做特别说明) 作用域链
|
JS的上下文环境有:
作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域,如果全局作用域也没有就会报错 |
说一说你对JS上下文栈和作用域链的理解?词法环境词法环境(Lexical Environment) 就是 ECMAScript 的代码环境。 它由 环境记录(Environment Record) 和 外部引用 组成。 它会根据 ECMAScript 代码来动态创建。 环境记录绑定了其关联的词法环境。其一共分了5类,分别如下:
词法环境分了三类:
执行上下文(Execution Contexts)执行上下文(Execution Contexts) 是 ECMAScript 代码 运行时(runtime) 的上下文环境。 在同一时间内,每次只能有一个 执行上下文(Execution Contexts) 运行。这称为 运行执行上下文(running execution context)。 执行上下文堆栈(execution context stack) 用于跟踪 执行上下文(Execution Contexts) 。 正在运行的 执行上下文(Execution Contexts) 始终是此堆栈的顶级元素。 每当控制从与当前运行的执行上下文相关联的可执行代码转移到与该执行上下文无关的可执行代码时,就创建新的执行上下文。 新创建的执行上下文被压入堆栈并成为正在运行的 执行上下文 。 所有执行上下文的状态组件(State Components)
作用域链(Scope chain)
从上面两个话题,我们可以知道,除 全局作用域(global scope) 外,每个作用域始终连接到其背后的一个或多个作用域,从而形成 作用域链(scope chain) 。全局作用域(global scope) 没有任何父级,这也是有意义的,因为它位于层次结构的顶部。 我们看下面的代码: const bestAvenger = "Iron man";
function a() {
const bestActor = "Neymar";
console.log(bestAvenger); // output:Iron man
function c() {
const bestProgrammingLanguage = "Html";
console.log(bestActor); // output:Neymar
b();
}
c();
}
function b() {
console.log(bestProgrammingLanguage); //**not defined error**
}
a(); 上面的代码会报错如下: 如上所述,作用域链(Scope chain) 始终是 词法 创建的。作用域 的父节点由 执行上下文(函数) 在代码中的词法或物理位置定义。 上面代码的 作用域链(Scope chain) 如下:
其实总结起来就是:
参考资料 |
上周这题还没有解答,这周补上哈。 |
上下文栈是用来存放执行上下文的,也可以理解为是一个存储函数调用的栈结构。 作用域链: |
执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。 Javascript 代码在运行的时候,就是在执行上下文中运行。 在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。 |
下为
环境记录是个抽象概念,词法环境属于环境记录的一种,相当于其子类。你这里说错了。
一个环境记录具有 |
No description provided.
The text was updated successfully, but these errors were encountered: