【javascript】执行上下文、作用域以及作用域链

本文详细讲解了执行上下文的概念、作用域分类及其在代码中的应用,包括全局和函数上下文,以及作用域链的形成和作用。通过实例演示,揭示了变量和函数访问的逻辑。此外,还探讨了作用域链在函数、闭包和跨作用域中的关键作用。
摘要由CSDN通过智能技术生成


前言

本文主要介绍执行上下文作用域以及作用域链的相关概念和知识


一、执行上下文

变量或函数的上下文决定了他们可以访问哪些数据以及行为。每个上下文都有一个关联的变量对象,而这个上下文钟定义的所有变量和函数都存在于这个对象上。

1. 作用域分类

  1. 全局上下文
    全局上下文就是我们常说的window对象,全局变量和函数都会成为window对象的属性和方法
  2. 函数上下文
    在函数执行的时候,会为这个函数创建函数执行上下文。

2. 代码分析

                            //1. 进入全局执行上下文
  var a = 10
  var b = function (x) {
    var b = 5
    c(x + b)              //3. 进入c执行上下文
  }
  var  c = function (y) {
    var c = 5
    console.log(a + c + y)
  }
  b(10)                    //2. 进入b函数执行上下文

2.1 全局上下文分析

  1. 代码执行前,首先会创建全局的执行上下文。
  2. 创建window变量对象,将全局变量和函数存到window中,并压入栈中。在这里插入图片描述
  3. 在执行var a = 10之前所做的预解析工作,可以观察到在全局中所定义的变量和函数都保存在了window中,并且值为undefined。截取部分window对象中保存的数据
  4. 全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器

2.2 函数上下文分析

  1. 在执行函数之前,创建函数执行上下文。

  2. 收集里面的所有变量和函数,保存在活动对象中(activation object)(如果是函数上下文,则活动对象用作变量对象),并压入栈中。
    在这里插入图片描述

  3. 观察到 这个活动对象中不仅收集了x和b 还对this进行了初始化。
    在这里插入图片描述

  4. 函数执行完毕后,将其从栈顶中弹出,将控制权返还给之前的执行上下文。

2.3 趁热打铁

依次输出什么

console.log('global begin: '+ i)
  var i = 1
  foo(1);
  function foo(i) {
    if (i == 4) {
      return;
    }
    console.log('foo() begin:' + i);
    foo(i + 1);
    console.log('foo() end:' + i);
  }
  console.log('global end: ' + i)

结果:
global begin: undefined
foo() begin: 1
foo() begin: 2
foo() begin: 3
foo() end :3
foo() end:2
foo() end 1
global end 1


二、作用域

1. 什么是作用域

作用域为可访问变量,对象,函数的集合

2. 作用域分类

  • 全局作用域
  • 函数作用域
  • eval作用域
  • 块级作用域(ES6)

3. 作用域的作用

隔离变量,不同作用域下同名变量不会有冲突


三.作用域链

1. 什么是作用域链

上下文中的代码在执行的时候,会创建变量对象的一个作用域链(scope chain)。这个作用域链决定了各级上下文中的代码访问变量和函数的顺序。

2. 作用域链产生过程

var color = "blue"; 
function changeColor() { 
	let anotherColor = "red"; 
 	function swapColors() { 
 	let tempColor = anotherColor; 
	 anotherColor = color; 
 	color = tempColor; 
 // 这里可以访问 color、anotherColor 和 tempColor 
 } 
 // 这里可以访问 color 和 anotherColor,但访问不到 tempColor 
 swapColors(); 
} 
// 这里只能访问 color 
changeColor();
  1. 在changeColor()这个函数定义的时候,就为其添加了一个[[Scopes]] 这个属性,其保存上一级的作用域链。在这个例子中上一级的作用域链就是全局作用域。
    在这里插入图片描述
  2. 在函数执行的时候,进入执行上下文环境,将这个函数的[[Scopes]]复制作为作用域链。
  3. 创建活动对象,并将活动对象添加到最前端。那么这个就是当前函数的作用域链。
    在这里插入图片描述
    注意图中的Closure是闭包的意思。
    下图是这个例子的作用域链的直观表示。
    在这里插入图片描述

3. 趁热打铁

练习1

  var x = 10;
  function fn() {
    console.log(x);
  }
  function show(f) {
    var x = 20;
    f();
  }
  show(fn);

打印结果为:10
分析思路:主要是看fn的作用域链,其作用域链是中第一个是其活动对象,由于里面不包含去一下一个作用域查找也就是全局作用域了),

练习2

  var fn = function () {
    console.log(fn)
  }
  fn()

  var obj = {
    fn2: function () {
      console.log(fn2)
    }
  }
  obj.fn2()

结果:ƒ () {console.log(fn) }
Uncaught ReferenceError: fn2 is not defined

分析:首先是fn,最后在全局作用域中找到了fn并打印。为什么fn2却没打印呢? 这是因为fn2()作用域链上只有它自己的活动对象(因为fn2在对象内部,而不是全局作用域中,没有被预解析到),而它活动对象里面没有fn2,最后只能报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值