首先,我们要知道执行环境和作用域是两个完全不同的概念。
函数的每次调用都有与之紧密相关的作用域和执行环境。从根本上来说,作用域是基于函数的,而执行环境是基于对象的(例如:全局执行环境即window对象)。
换句话说,作用域涉及到所被调用函数中的变量访问,并且不同的调用场景是不一样的。执行环境始终是this关键字的值,它是拥有当前所执行代码的对象的引用。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
执行环境(也称执行上下文–execution context)
当JavaScript解释器初始化执行代码时,它首先默认进入全局执行环境,从此刻开始,函数的每次调用都会创建一个新的执行环境。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中(execution stack)。在函数执行完后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript程序中的执行流正是由这个便利的机制控制着。
执行环境可以分为创建和执行两个阶段。在创建阶段,解析器首先会创建一个变量对象(variable object,也称为活动对象 activation object),它由定义在执行环境中的变量、函数声明、和参数组成。在这个阶段,作用域链会被初始化,this的值也会被最终确定。在执行阶段,代码被解释执行。
Demo:
<script type="text/javascript"> function Fn1(){ function Fn2(){ alert(document.body.tagName);//BODY //other code... } Fn2(); } Fn1(); //code here </script>
小结
当javascript代码被浏览器载入后,默认最先进入的是一个全局执行环境。当在全局执行环境中调用执行一个函数时,程序流就进入该被调用函数内,此时JS引擎就会为该函数创建一个新的执行环境,并且将其压入到执行环境堆栈的顶部。浏览器总是执行当前在堆栈顶部的执行环境,一旦执行完毕,该执行环境就会从堆栈顶部被弹出,然后,进入其下的执行环境执行代码。这样,堆栈中的执行环境就会被依次执行并且弹出堆栈,直到回到全局执行环境。
此外还要注意一下几点:
单线程
同步执行
唯一的全局执行环境
局部执行环境的个数没有限制
每次某个函数被调用,就会有个新的局部执行环境为其创建,即使是多次调用的自身函数(即一个函数被调用多次,也会创建多个不同的局部执行环境)。
作用域
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链包含了执行环境栈中的每个执行环境对应的变量对象。通过作用域链,可以决定变量的访问和标识符的解析。
注意:全局执行环境的变量对象始终都是作用域链的最后一个对象。
在访问变量时,就必须存在一个可见性的问题(内层环境可以访问外层中的变量和函数,而外层环境不能访问内层的变量和函数)。更深入的说,当访问一个变量或调用一个函数时,JavaScript引擎将不同执行环境中的变量对象按照规则构建一个链表,在访问一个变量时,先在链表的第一个变量对象上查找,如果没有找到则继续在第二个变量对象上查找,直到搜索到全局执行环境的变量对象即window对象。这也就形成了Scope Chain的概念。
作用域链图,清楚的表达了执行环境与作用域的关系(一一对应的关系),作用域与作用域之间的关系(链表结构,由上至下的关系)。
Demo:
var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; // 这里可以访问color, anotherColor, 和 tempColor } // 这里可以访问color 和 anotherColor,但是不能访问 tempColor swapColors(); } changeColor(); // 这里只能访问color console.log("Color is now " + color);
上述代码一共包括三个执行环境:全局执行环境、changeColor()的局部执行环境、swapColors()的局部执行环境。
全局环境有一个变量color和一个函数changecolor();
changecolor()函数的局部环境中具有一个anothercolor属性和一个swapcolors函数,当然,changecolor函数中可以访问自身以及它外围(即全局环境)中的变量;
swapcolor()函数的局部环境中具有一个变量tempcolor。在该函数内部可以访问上面的两个环境(changecolor和window)中的所有变量,因为那两个环境都是它的父执行环境。
上述代码的作用域链如下图所示:
从上图发现。内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数。
标识符解析(变量名或函数名搜索)是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后(全局执行环境)回溯,直到找到标识符为止。
执行环境与作用域的区别与联系
执行环境为全局执行环境和局部执行环境,局部执行环境是函数执行过程中创建的。
作用域链是基于执行环境的变量对象的,由所有执行环境的变量对象(对于函数而言是活动对象,因为在函数执行环境中,变量对象是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO(变量对象)的角色。)共同组成。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。
小练习
<script type="text/javascript"> (function(){ a= 5; console.log(window.a);//undefined var a = 1;//这里会发生变量声明提升 console.log(a);//1 })(); </script>
window.a之所以是undefined,是因为var a = 1;发生了变量声明提升。相当于如下代码:
<script type="text/javascript"> (function(){ var a;//a是局部变量 a = 5;//这里局部环境中有a,就不会找全局中的 console.log(window.a);//undefined a = 1;//这里会发生变量声明提升 console.log(a);//1 })(); </script>
以上就是小编为大家带来的老生常谈原生JS执行环境与作用域全部内容了,希望大家多多支持小牛知识库~
本文向大家介绍老生常谈js-react组件生命周期,包括了老生常谈js-react组件生命周期的使用技巧和注意事项,需要的朋友参考一下 组件的生命周期可分成三个状态: •Mounting:已插入真实 DOM •Updating:正在被重新渲染 •Unmounting:已移出真实 DOM 生命周期的方法有: •componentWillMount 在渲染前调用,在客户端也在服务端。 •compone
本文向大家介绍老生常谈 js中this的指向,包括了老生常谈 js中this的指向的使用技巧和注意事项,需要的朋友参考一下 在js中this的指向对于新手来说一定是个难题,但是如果你真正理解了的话,也就没什么问题啦,下面就来讲讲this吧。 JS中,this的值取决于调用的模式(调用对象),而JS中共有4种调用模式: 1.函数调用模式 当一个函数不是一个对象的属性时,当作函数俩调用,这时函数内的t
本文向大家介绍老生常谈的跨域处理,包括了老生常谈的跨域处理的使用技巧和注意事项,需要的朋友参考一下 阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自己搞懂这方面的知识,走起。 什么是跨域 在JavaScript中,有一个很重要的安全性限制,被称为“Same-Orig
本文向大家介绍老生常谈JavaScript数组的用法,包括了老生常谈JavaScript数组的用法的使用技巧和注意事项,需要的朋友参考一下 JavaScript数组简介 JavaScript中的数组与其他语言中的数组是不同的,主要体现在: 数组中存储的各项可以是不同类型的数据 数组的大小是动态变化的,当新增项时或移除项时可以动态的改变大小来容纳当前数据项 在JavaScript中创建数组 在Jav
本文向大家介绍老生常谈Scanner的基本用法,包括了老生常谈Scanner的基本用法的使用技巧和注意事项,需要的朋友参考一下 需求:键盘录入一个月份,输出该月份对应的季节。 一年有四季 3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12,1,2 冬季 分析: A:键盘录入一个月份,用Scanner实现 B:判断该月份是几月,根据月份输出对应的季节 当然了还有很多其他的方法
本文向大家介绍js老生常谈之this,constructor ,prototype全面解析,包括了js老生常谈之this,constructor ,prototype全面解析的使用技巧和注意事项,需要的朋友参考一下 前言 javascript中的this,constructor ,prototype,都是老生常谈的问题,深入理解他们的含义至关重要。在这里,我们再来复习一下吧,温故而知新! this