JavaScript核心概念之执行上下文和栈
现在想改变一下写作方式,以问答的形式来讲解这些枯燥无味的知识,尽量把每一个为什么都讲透,每个知识点都不迷惑。
桃翁桃翁,问个问题呢,据说 js 里面有个执行上下文,这个概念是个什么东东哦?据说挺重要的,给我科普科普呗。
Emm… 这个概念非常的抽象,简单来说呢,就是 JS 在执行某段代码的时候做的一些事情。
具体做的事情就是定义了变量或函数有权访问的其他数据决定了它们各自的行为(作用域链)。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中(变量包括 this、arguments)。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
哇,还是好抽象啊,你能不能画个图举个栗子呢?
在之前说的执行上下文就是解释器在执行 JS 某段代码的时候做的一些事,那么首先我们把代码分个类。
Global 代码:代码第一次执行时默认的环境。
Function 代码:执行到一个函数中。
Eval 代码:文本在eval函数内部执行。
看到这个图相信现在分清楚各种类型的代码,每种类型代码会都会产生执行上下文,我们把 Global 代码产生的执行环境叫「全局执行上下文」,把 Function 代码产生的执行环境叫「执行上下文」吧,Eval 代码不考虑。
那我看这个图似乎有很多执行上下文(execution context),这个具体是怎么来的呢?
全局执行上下文只有一个,而执行环境的话是每次函数调用都会产生一个执行上下文。注意要调用才会产生哦,不调用是不会产生的。
那这个执行上下文基本知道是个什么东西了,那执行上下文栈又是啥呢?
见名知意,执行上下文栈就是执行上下文(包含全局执行上下文)形成的栈嘛。
那为什么要有这个执行上下文栈呢?
浏览器中 JavaScript 解释器是单线程的,这就是说同一时间代码只会做一件事,那么创建这么多执行上下文,又不能同一时间执行多个上下文,所以就必须要有个顺序,这个顺序就是就是先进后出,这很明显就是一个栈结构嘛。
那我就疑惑了,为啥要先进后出,不先进先出呢?
我们分析一下图一的代码,结合上图,首先我们看图 1,解释代码的时候首先创建的就是全局上下文,然后再创建 person 的执行上下文,然后再创建 firstName 的上下文,然后再执行完毕 firstName ,就把 firstName 的上下文弹出,再 创建 lastName 的上下文,然后执行完毕,再弹出 lastName 的上下文,然后执行完 person 的上下文,再弹出 person 的上下文,再执行全局上下文,然后全局上下文弹出。
如下是一张经典的执行上下文栈的图。
默认进入全局上下文。如果你的全局代码中调用了一个函数,那么程序将会进入这个被调用函数的上下文,创建一个新的执行上下文,并把当前上下文放到栈顶。浏览器总是会把当前执行上下文放到栈的顶部,一旦函数执行完成,这个执行上下文就会从栈中移除,返回到栈中的下一个上下文。
这些大概明白了,不过你说在创建执行上下文做的那些事儿,我还是有点迷糊,能再详细说说吗?
那我们首先看点代码:
// 例1 console.log(a); // 报错,a is not defined // 例2 console.log(a); // undefined var a; // 例 3 console.log(a); // undefined var a = 666; // 例 4 console.log(this); // window 对象 // 例 5 function foo(x) { console.log(arguments); // [666] console.log(x); // 666 } foo(666); // 例 6 // 函数表达式 console.log(foo); // undefined var foo = function foo() {} // 例 7 // 函数声明 console.log(foo); // function() {} function foo() {}
这 7 个例子相信大家对这些答案都是没有疑惑的,最基础的东西,例 1 报错,a 未定义,很正常。例 2、例 3 输出都是 undefined,说明浏览器在执行 console.log(a) 时,已经知道了 a 是 undefined,但却不知道 a 是 666(例 3)。
看例 4 就知道,当执行这条语句的时候 this 已经被赋值了。
在例 5 中展示了在函数体的语句执行之前,arguments 变量和函数的参数都已经被赋值。从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
然后就是例 6,例 7 中可以看出函数表达式跟变量声明一样,只是给变量赋值成 undefined,而函数声明会将会把函数整个赋值了。
总结在执行上下文做的赋值事情
变量、函数表达式——变量声明,默认赋值为undefined;
this——赋值;
函数声明——赋值;
执行上下文就介绍到这里,如果你对相关知识还是感到迷惑,比如当在创建执行上下文的时候还有作用域,以及变量对象等概念,后面再一一介绍,不要担心,跟着我的文章走,这块一定能啃动。
原文发布时间为:2018-09-21
原文作者: 桃翁
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从统计到概率,入门者都能用Python试验的机器学习基础 CDA数据分析师 4天前
要学习统计,就不可避免得先了解概率问题。概率涉及诸多公式和理论,容易让人迷失其中,但它在工作和日常生活中都具有重要作用。先前我们已经讨论过描述性统计中的一些基本概念,现在,我们将探讨统计和概率的关系。 前提条件: 与上一篇博客类似,本文不要求读者具备统计知识,但至少要对 Python 有一个基本的了解。考虑到读者可能不太了解 for 循环和列表, 下面将先对它们做个简单的介绍。 什么是概率? 从最基本的层面上来说,概率要回答的是一个这样的问题:「一个事件发生的几率是多少?」为了计算某个事件发生的几率,我们还要考虑其它所有可能发生的事件。 概率问题的典型代表是扔硬币。在扔硬币的过程中,只会产生两种结果: 1. 正面朝上 2. 反面朝上 这两种结果构成了一个样本空间,即所有可能结果的集合。为了计算一个事件发生的概率,我们要统计该事件发生(比如将硬币掷为正面朝上)的次数,并用它除以总试验次数。因此,概率会告诉我们,把一枚硬币掷为正面朝上或反面朝上的几率为 1/2。通过观察可能发生的事件,概率可以为我们提供一个预测事件发生频率的框架。 然而,即使结果看起来很明显,但如果我们真的试着去扔一些硬币...
- 下一篇
不踩坑的Python爬虫:如何在一个月内学会爬取大规模数据
Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫、学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多,另一方面,像 Python这样的编程语言提供越来越多的优秀工具,让爬虫变得简单、容易上手。 利用爬虫我们可以获取大量的价值数据,从而获得感性认识中不能得到的信息,比如: 知乎:爬取优质答案,为你筛选出各话题下最优质的内容。 淘宝、京东:抓取商品、评论及销量数据,对各种商品及用户的消费场景进行分析。 安居客、链家:抓取房产买卖及租售信息,分析房价变化趋势、做不同区域的房价分析。 拉勾网、智联:爬取各类职位信息,分析各行业人才需求情况及薪资水平。 雪球网:抓取雪球高回报用户的行为,对股票市场进行分析和预测。 爬虫是入门Python最好的方式,没有之一。Python有很多应用的方向,比如后台开发、web开发、科学计算等等,但爬虫对于初学者而言更友好,原理简单,几行代码就能实现基本的爬虫,学习的过程更加平滑,你能体会更大的成就感。 掌握基本的爬虫后,你再去学习Python数据分析、web开发甚至机器学习,都会更得心应手。因为这个过程中,Python基本语法、库的使用,以...
相关文章
文章评论
共有0条评论来说两句吧...