专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
OSC开源社区  ·  Bun ... ·  18 小时前  
程序员的那些事  ·  OpenAI ... ·  昨天  
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  4 天前  
程序员的那些事  ·  成人玩偶 + ... ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

前端进击的巨人(一):执行上下文与执行栈,变量对象

SegmentFault思否  · 公众号  · 程序员  · 2019-02-21 08:00

正文

写在开篇

已经不敢自称前端小白,曾经吹过的牛逼总要一点点去实现。

正如前领导说的,自己喝酒吹过的牛皮,跪着都得含着泪去实现。

那么没有年终完美总结,来个新年莽撞开始可好。

进击巨人系列开篇,不忘初心,砥砺前行。

理解执行上下文

执行上下文(Execution Context): 函数执行前进行的准备工作(也称执行上下文环境)

运行JavaScript代码时,当代码执行进入一个环境时,就会为该环境创建一个执行上下文,它会在你运行代码前做一些准备工作,如确定作用域,创建局部变量对象等。

具体做了什么先按下不表,先来看下JavaScript执行环境有哪些?

JavaScript中执行环境
  1. 全局环境

  2. 函数环境

  3. eval函数环境 (已不推荐使用)

那么与之对应的执行上下文类型同样有3种:

执行上下文的类型
  1. 全局执行上下文

  2. 函数执行上下文

  3. eval函数执行上下文

JavaScript运行时首先会进入全局环境,对应会生成全局上下文。程序代码中基本都会存在函数,那么调用函数,就会进入函数执行环境,对应就会生成该函数的执行上下文。

先插播一个知识点: JS是"单线程" ! "单线程" ! "单线程" !

简单理解下单线程,就是同个时间段只能做一件任务,完成之后才可以继续下一个任务。正如女朋友只有一个,各位面向对象的小伙伴们你们说对不对?有女票的必须说没毛病。

既然是这样,必须要有一个排队机制,不然就会出现几个流氓霸着车道不让过,"还有王法么?"

JS中管理多个执行上下文

函数编程中,代码中会声明多个函数,对应的执行上下文也会存在多个。在JavaScript中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈(Call Stack)。

在说明执行栈前,先来补下 "栈数据结构" 知识点。

栈数据结构

借助前端大神的例子,用乒乓球盒子来理解栈的存取方式(这个例子让我彻底记住了栈数据结构)。

栈遵循 "先进后出,后进先出" 的规则,或称 LIFO ("Last In First Out") 规则。

如图所示,我们只能从栈顶取出或放入乒乓球,最先放进盒子的总是最后才能取出。栈中 "放入/取出" ,也可称为 "入栈/出栈"

总结栈数据结构的特点:

  1. 后进先出,先进后出

  2. 出口在顶部,且仅有一个

执行栈(函数调用栈)

理解完栈的存取方式,我们接着分析JavaScript中如何通过栈来管理多个执行上下文。

程序执行进入一个执行环境时,它的执行上下文就会被创建,并被推入执行栈中(入栈);程序执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。

因为JS执行中最先进入全局环境,所以处于 "栈底的永远是全局环境的执行上下文" 。而处于 "栈顶的是当前正在执行函数的执行上下文" ,当函数调用完成后,它就会从栈顶被推出(理想的情况下,闭包会阻止该操作,闭包后续文章深入详解)。

"全局环境只有一个,对应的全局执行上下文也只有一个,只有当页面被关闭之后它才会从执行栈中被推出,否则一直存在于栈底。"

文字太多不如上代码:

  1. function foo () {

  2.    function bar () {

  3.        return 'I am bar';

  4.    }

  5.     return bar();

  6. }

  7. foo();

执行上下文的生命周期

执行上下文的生命周期有两个阶段:

  1. 创建阶段( 进入 执行上下文)

  2. 执行阶段(代码 执行

创建阶段:函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段。

执行阶段:执行函数中代码时,此时执行上下文进入执行阶段。

创建阶段的操作

1、创建变量对象

  • 函数环境会初始化创建 Arguments 对象( 并赋值

  • 函数声明( 并赋值

  • 变量声明,函数表达式声明( 未赋值

2、确定this指向( this由调用者确定

3、确定作用域( 词法环境决定,哪里声明定义,就在哪里确定

执行阶段的操作

1、变量对象赋值

  • 变量赋值

  • 函数表达式赋值

2、调用函数

3、顺序执行其它代码

看到这里,我们不经会问变量对象是什么鬼,它与代码中常见的函数声明,变量声明有神马关系???

变量对象和活动对象的区别:

当进入到一个执行上下文后,这个变量对象才会被激活,所以叫活动对象(AO),这时候活动对象上的各种属性才能被访问。

"创建阶段对函数声明做赋值,变量及函数表达式仅做声明,真正的赋值操作要等到执行上下文代码执行阶段。"

代码例子1:变量提升

  1. function foo() {

  2.  console.log(a);         // 输出undefined

  3.  var a = 'I am here';    // 赋值

  4. }

  5. foo();


  6. // 实际执行过程

  7. function foo() {

  8.  var a;                // 变量声明,var初始化undefined

  9.  console.log(a);

  10.  a = 'I am here';     // 变量重新赋值

  11. }

代码例子2:函数声明优先级

  1. function foo() {

  2.    console.log(bar);

  3.    var bar = 20;

  4.    function bar() {

  5.      return 10;

  6.    }

  7.    







请到「今天看啥」查看全文