首先,咱们通常被"执行上下文","执行上下文环境","上下文环境","执行上下文栈"这些名词搞混。那我们一一来揭秘这些名字的含义。

这一块一直比较晦涩难懂,还是需要仔细去斟酌斟酌。

什么是执行上下文(也叫做“执行上下文环境”,“上下文环境”)?

咱们还是先看代码。

console.log(a)  // undefined
var a = 100 fn('bella') // 'bella' 20
function fn(name) {
age = 20
console.log(name, age)
var age
} console.log(b); // 这里报错
// Uncaught ReferenceError: b is not defined

第一个console输出 undefined,说明浏览器在执行console.log(a)的时候,已经知道a的存在的,但是不知道a的值。

第二个fn("bella")输出 "bella" 20,说明浏览器在执行的时候已经知道fn函数了,并且执行了

第三个console报错,b is noe defined。说明没有找到b

那么可以看出来,浏览器在执行之前做了一些准备工作。

那做了些什么准备工作:

  • 全局上下文环境: 变量定义,函数声明
  • 函数上下文环境(函数内部):变量定义,函数声明,this,arguments

下面这个例子很多地方都用来讲解执行上下文和上下文栈。

 // 这是一个压栈出栈的过程--执行上下文栈
let a = 10; // 1、进入全局上下文环境
let fn;
let bar = function(x) {
let b = 5
fn(x + b) // 3、进入fn函数上下文环境
};
fn = function(y) {
let c = 5
console.log(y + c)
} bar(10) // 2、 进入bar函数上下文

这里引出了执行上下文栈的概念,上下文栈就是压栈和出栈的过程

1.在代码执行之前,首先创建全局上下文环境

    // 全局上下文环境
a: undefined
fn: undefined
bar: undefined,
this: window

2.然后执行代码,在代码执行到12之前,全局上下文中的变量在执行中被赋值

    // 全局上下文环境
a: 10
fn: function
bar: function,
this: window

然后执行13行代码,调用bar函数,会创建一个新的执行上下文环境。并将这个bar上下文环境压栈,并设置为活动状态

   // bar函数上下文环境
b: undefined
x: 10
arguments: [10]
this: window

3.然后执行到第6行代码,调用fn的时候,会创建一个新的执行上下文。并将这个fn上下文环境压栈,并设置为活动状态。

// fn函数上下文环境
c: undefined
y: 15
arguments: [15]
this: window

4.fn执行完毕后,调用fn函数生成的fn上下文环境出栈,销毁。然后bar出栈销毁。然后全局上下文出栈销毁

理解完了执行上下文,再看看this

相信都知道这句话,谁调用函数,this就指向谁。那么我们理解下this:

    var a = {
name: 'A',
fn: function() {
console.log(this.name)
}
}
a.fn() // this === a
a.fn.call({
name: 'B'
}) // this === {name: 'B'}
var fn1 = a.fn
fn1() // this === window

this: this的值只有在执行的时候才能确认,定义的时候不能确认。因为this是执行上下文的一部分,而执行上下文需要再代码执行之前确定。

this执行会有不同,主要集中在这几个场景中:
  1. 作为构造函数执行,构造函数中
  2. 作为对象属性执行,上述代码中a.fn()
  3. 作为普通函数执行,上述代码中fn1()
  4. 用于call apply bind,上述代码中a.fn.call()

作用域

ES6之前没有块级作用域,除了全局作用域,函数会创建自己的作用域。
作用域在函数定义的时候已经确定了,不是在函数调用确定(区别于执行上下文环境,this是执行上下文环境中的)
作用域只是一个“地盘”,其中没有变量。变量是通过作用域对应的执行上下文环境中的变量对象来实现的。所以作用域是静态的,而执行上下文是动态的
有闭包存在的时候,一个作用域存在两个上下文环境也是有的。
也就是说,作用域只是用于划分你在这个作用域里面定义的变量的有效范围,出了这个范围就无效
 

作用域链

函数在定义的时候就确定了函数体内部自由变量的作用域
自由变量:比如a,在fn作用域使用,但是并没有在fn作用域定义。这就是自由变量
let a = 100
function fn() {
let b = 20
function bar() {
console.log(a + b) // a是自由变量
}
return bar
} let x = fn(), b = 200
x()
 
那么自由变量是如何得到的?这就引出了作用域链
bar要取得a的值,就要在bar函数的作用域中取值,如果没有,就往上找,找到fn作用域内,也没有定义a,继续往上找,就找到全局作用域,找到就结束了。这就是作用域链
 
 
讲完这些,我们再通过一个例子来理解闭包
 function F1() {
var a = 100
return function () {
console.log(a)
}
}
var f1 = F1()
var a = 200
f1()
自由变量将从作用域链中去寻找,但是 依据的是函数定义时的作用域链,而不是函数执行时,以上这个例子就是闭包。
怎么理解依据的是函数定义时的作用域链,而不是函数执行时这句话?
 
调用第9行之后
如果按照执行时,就输出的时200
但是作用域都是在定义时就生成了,所以f1回去再定义function的作用域去找,因此输出100.
 
理解闭包之后,我们就看看闭包的主要场景:
  • 函数作为返回值,上面的例子就是
  • 函数作为参数传递

js - 执行上下文和作用域以及闭包的更多相关文章

  1. Js 执行上下文和作用域

    1.执行上下文和执行栈 执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行. 执行上下文的生命周期包括三个阶 ...

  2. js执行环境、作用域

    js执行环境.作用域 执行环境:是javascript中的一个重要的概念,<javascript高级程序设计第三版>的定义是:执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行 ...

  3. JS三座大山再学习 ---- 作用域和闭包

    本文已发布在西瓜君的个人博客,原文传送门 作用域 JS中有两种作用域:全局作用域|局部作用域 栗子1 console.log(name); //undefined var name = '波妞'; v ...

  4. 我不知道的js(一)作用域与闭包

    作用域与闭包 作用域 什么是作用域 作用域就是一套规则,它负责解决(1)将变量存在哪儿?(2)如何找到变量?的问题 作用域工作的前提 谁赋予了作用域的权利?--js引擎 传统编译语言编译的过程 分词/ ...

  5. JS执行环境,作用域链及非块状作用域

    JS中的执行环境,顾名思义就是变量或函数所执行时的环境.在我的理解中,执行环境和作用域相差不大. 每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中.而在函数执行之后 ...

  6. 【repost】 原生JS执行环境与作用域深入理解

    首先,我们要知道执行环境和作用域是两个完全不同的概念. 函数的每次调用都有与之紧密相关的作用域和执行环境.从根本上来说,作用域是基于函数的,而执行环境是基于对象的(例如:全局执行环境即window对象 ...

  7. 浅谈JS执行环境及作用域

     今天刚刚开通博客,也是第一次写博文,略感紧张.作为一个表达能力弱弱的人来说,自己花三分钟理解一个知识点,当别人问起时,也许需要30分钟才只是让别人知道自己在说什么,一点也不夸张,希望在博客上可以练习 ...

  8. JS 执行环境与作用域链

    1.执行环境 JavaScript 代码都是在执行环境中被执行的.执行环境是一个概念,一种机制,用来完成JavaScript运行时在作用域.生命周期等方面的处理,它定义了变量或函数是否有权访问其他数据 ...

  9. js原型链,作用域,闭包讲解

    当面试的时候遇到问原型链,闭包,还有作用域,直接 拿张纸和笔把原型链画出来,闭包跟作用域直接用笔写几道题出来加深理解(因为我们是理科生,图形和题目以及控制台输出结果才是最直观的方法) 问:什么是原型链 ...

随机推荐

  1. ufile的硬盘

    参考: https://docs.ucloud.cn/compute/uhost/introduction/disk UFS: https://docs.ucloud.cn/storage_cdn/u ...

  2. PTA(Advanced Level)1036.Boys vs Girls

    This time you are asked to tell the difference between the lowest grade of all the male students and ...

  3. Spring 中的几个常用的钩子接口

    1.Aware接口 Awear 这个单词的意思是知道的,所以可以猜想以Awear 结尾的接口意思可以把他知道的东西告诉我们.常用的Awear接口有 ApplicationContextAware和 B ...

  4. 【Python】【demo实验1】【Python运行时强制刷新缓冲区,输出信息】(Python3 应不存在类似情况)

    [原文] 需求:打印一颗 ”*” 休息1s 代码如下: #!/usr/bin/python #coding=utf-8 ''' 暂停1s输出 ''' import time def printStar ...

  5. 小菜鸟之java JDBC编程

    JDBC技术 百度简介 :  JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一 ...

  6. C++学习 之 类中的特殊函数和this指针(笔记)

    1.构造函数 构造函数是一种特殊的函数,它在对象被创建时被调用,与类同名无返回类型,可以被重载.构造函数的可以在类内实现也可以在类外实现. 构造函数的声明类似于下面的代码: class Human { ...

  7. # N数码问题

    N数码问题 首先,先贯彻一个理念.奇偶性很神奇,对于一类问题,如果属于同种性质(奇偶性相同),那么它们就是完全相同(这个在某种意义上说)的,,一些问题如果奇偶性相同那么里面涉及的问题都是等价的. 数码 ...

  8. python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05

    反射 reflect 反射(reflect)其实是反省,自省的意思 反省:指的是一个对象应该具备可以检测.修改.增加自身属性的能力 反射:通过字符串获取对象或者类的属性,进行操作 设计框架时需要通过反 ...

  9. Eclipce远程调试

    1.注意: root权限启动的进程不支持远程调试,也有很多隐患,支持最高权限,应用普通用户启动,又原先安装Tomcat是使用的root权限,普通用户对root安装的软件没有执行权限,可以修改相应权限, ...

  10. 【原创】大叔经验分享(55)spark连接kudu报错

    spark-2.4.2kudu-1.7.0 开始尝试 1)自己手工将jar加到classpath spark-2.4.2-bin-hadoop2.6+kudu-spark2_2.11-1.7.0-cd ...