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

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

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

咱们还是先看代码。

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. Python:Django 项目中可用的各种装备和辅助

    1 Redis 数据库 2 MySQL 数据库 3 前端服务器 live-server 4 定时任务 django-crontab扩展 5 Docker 容器 --用来运行 FastDFS 分布式文件 ...

  2. DOM事件练习 II

    select框联动效果 需求:当在textarea中输入内容,点击留言按钮,会添加到浏览器中,最新留言出现在最顶端. <!DOCTYPE html> <html lang=" ...

  3. 8 Spring / Spring MVC / Mybatis 框架相关知识点

    1)Spring 的 IOC 和 AOP 有了解吗? IOC:控制反转,不需要手动 new 对象,将其交给 Spring 容器,降低程序耦合度. AOP:面向切面编程,动态代理技术.

  4. Opencv-python3.3版本安装

    因为目前为止(2019.11.17)opencv最新版本为4.1,因此直接pip install opencv-python的话,无法安装想要的版本(老师推荐3.X) 上清华镜像查找opencv-py ...

  5. Java中流的操作练习

    文件中的学生信息 学生信息存储在TXT文件中,我们需要对信息进行基本的,增.删.改.查,全部显示操作. 1.学生类/Student package com.yujiao.student; public ...

  6. MYSQL—第一部分(简介和windows版本的安装)

    一.概述 1.什么是数据库 ? 答:数据的仓库,如:在自己编写的程序中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access.MS SQLServe ...

  7. redis缓存穿透00

    缓存穿透 缓存穿透,是指查询一个数据库一定不存在的数据.正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存.如果数据库 ...

  8. # IDEA相关知识

    目录 IDEA相关知识 安装目录下: 配置目录下: 工程目录下: 名词解释 IDEA相关知识 安装目录下: bin:启动文件,配置信息,IDEA的一些属性信息 jre64:IDEA自带的运行环境 li ...

  9. Laravel之杂记

    1.composer设置国内镜像加速 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 2 ...

  10. 从入门到自闭之Python时间模块

    time模块:import time time.time():时间戳,是一个浮点数,按秒来计算 time.sleep():睡眠,程序暂停多少秒执行 python中时间日期格式化符号: 必背 %y 两位 ...