在学习JavaScript作用域概念之前,首先要明白几个概念:执行环境、变量对象、作用域链。

一、JavaScript执行环境(execution context):

在《Professional JavaScript for Web Developers》一书中写到:

The concept of execution context, referred to as contextfor simplicity, is of the utmost importance in JavaScript. The execution context of a variable or

function defines what other data it has access to, as well as how it should behave. Each execution context has an associated variable objectupon which all > of its defined variables and functions exist. This object is not accessible by code but is used behind the scenes to handle data.

在对应的《JavaScript高级程序设计》一书翻译是这样的:

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保> 存在这个对象中。虽然我们编写的代码无法访问这个对象,但是解析器在处理数据时会在后台使用它。

全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完后,该环境被销毁,保存在其中的 > 所有变量和函数定义也随之销毁。(全局执行环境直到应用程序退出-例如关闭网页或者浏览器时才会被销毁。

二、全局作用域和局部作用域

1.全局作用域(Global Scope):在代码中任何地方都能访问到的对象拥有全局作用域

(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域

var authorName="山边小溪";
function doSomething(){
var blogName="梦想天空";
function innerSay(){
alert(blogName);
}
innerSay();
}
alert(authorName); //山边小溪
alert(blogName); //脚本错误
doSomething(); //梦想天空
innerSay() //脚本错误

(2)所有末定义直接赋值的变量自动声明为拥有全局作用域

function doSomething(){
var authorName="山边小溪";
blogName="梦想天空";
alert(authorName);
}
doSomething(); //山边小溪
alert(blogName); //梦想天空
alert(authorName); //脚本错误

变量blogName拥有全局作用域,而authorName在函数外部无法访问到。

(3)所有window对象的属性拥有全局作用域

一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等

2.局部作用域:和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域,例如下列代码中的blogName和函数innerSay都只拥有局部作用域。

function doSomething(){
var blogName="梦想天空";
function innerSay(){
alert(blogName);
}
innerSay();
}
alert(blogName); //脚本错误
innerSay(); //脚本错误

我们再来看一个具体的实例:

var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问 color、anotherColor 和 tempColor
}
// 这里可以访问 color 和 anotherColor,但不能访问 tempColor
swapColors();
}
// 这里只能访问 color
changeColor();

以上代码共涉及 3 个执行环境:全局环境、 changeColor() 的局部环境和 swapColors() 的局部

环境。全局环境中有一个变量 color 和一个函数 changeColor() 。 changeColor() 的局部环境中有一个名为 anotherColor 的变量和一个名为 swapColors() 的函数,但它也可以访问全局环境中的变量 color 。 swapColors() 的局部环境中有一个变量 tempColor ,该变量只能在这个环境中访问到。无论全局环境还是 changeColor() 的局部环境都无权访问 tempColor 。然而,在 swapColors() 内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。图 4-3 形象地展示了前面这个例子的作用域链。

图中的矩形表示特定的执行环境。其中,内部环境可以通过作用域链访问所有的外部环境,但

外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性、有次序的。每个环境都

可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个

执行环境。对于这个例子中的 swapColors() 而言,其作用域链中包含 3 个对象: swapColors() 的变量对象、 changeColor() 的变量对象和全局变量对象。 swapColors() 的局部环境开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则再搜索上一级作用域链。 changeColor() 的作用域链中只包含两个对象:它自己的变量对象和全局变量对象。这也就是说,它不能访问swapColors() 的环境。

三、JavaScript作用域链(scope chain)

作用域链创建:

当代码在一个环境中执行时,会创建变量对象的一个作用域。

作用域链功能:

作用域链:保证对执行环境有权访问的所有变量和函数的有序访问。

作用域链顺序:

1. 作用域的前端,始终是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象:在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中不存在)

2. 作用域链中的下一个变量对象来自包含(外部)环境
3. 再下一个变量对象则来自下一个包含环境。
4. 这样,一直延续到全局执行环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象。

解析

标识符解析是沿着作用域链一级一级地搜索标识符的过程。

搜索

搜索过程始终是从作用域的前端开始,逐级向后回溯,直至找到标识符为止(找不到,通常会报错)。

举例

任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现.

在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.

在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.

 var func = function(lps, rps){
var name = 'laruence';
........
}
func();

解析

在执行func的定义语句的时候, 会创建一个这个函数对象的[[scope]]属性, 并将这个[[scope]]属性, 链接到定义它的作用域链上, 此时因为func定义在全局环境, 所以此时的[[scope]]只是指向全局活动对象window active object.

在调用func的时候, 会创建一个活动对象(假设为aObj, 由JS引擎预编译时刻创建),并创建arguments属性, 然后会给这个对象添加俩个命名属性aObj.lps, aObj.rps; 对于每一个在这个函数中申明的局部变量和函数定义, 都作为该活动对象的同名命名属性.

然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。

然后将这个活动对象做为scope chain的最前端, 并将func的[[scope]]属性所指向的,定义func时候的顶级活动对象, 加入到scope chain.

有了上面的作用域链, 在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义

作用域其实是Function对象实例的一个内部属性【Scope】。函数被创建时候【Scope】这个内部属性包含了这个函数的作用域内所有对象的集合,这个集合叫做作用域链,作用域链决定了函数能访问到那些数据。

<script type="text/javascript">
function add(a,b)
{
var sum = a+b;
return sum;
}
</script>

 这是一个非常常见的函数,我们先定义好这个函数,记住先不执行它,当页面被加载,这个函数也会被初始化,记得前面我讲过,这样定义的函数是属于window的,加载页面时候会预编译的,这时候函数add的作用域链如下图:

四、执行上下文顺序

在进入执行上下文时候,会按照顺序执行下面的操作:

1.创建活动对象

活动对象是在进入执行上下文时候创建出来的,并且与新的执行上下文关联在一起。在初始化构造函数的时候,该对象包含一个arguments属性。激活对象在变量初始化也会用到。

2.创建作用域链

每一个函数都有一个内部属性[[scope]],它的值是一个包含多个对象的链。这个属性的具体指与函数的创建方式和代码中的位置有很大的关系。这一步的操作是把上一步创建的激活对象添加到函数的[[scope]]属性对应的链的前面。

3.变量初始化

对函数中需要用到的变量进行初始化。初始化时使用到的对象是前面创建的活动对象,不过这时还不能称作为变量对象。函数的实参、内部函数的局部变量是会被初始化

的。局部变量是在变量对象创建的过程中创建了同名的属性,这个属性值为undefined,在函数执行的过程中才会被真正的赋值。

ife task0003学习笔记(一):JavaScript作用域的更多相关文章

  1. ife task0003学习笔记(三):JavaScript闭包

    一.this易错分析 在学习闭包的时候,有一个概念this很重要,关于this的理解,下面3种情况:this指向谁? fn.call(obj1); obj2.fn() fn() 答案是obj1 obj ...

  2. ife task0003学习笔记(四):JavaScript构造函数

    JavaScript创建对象主要是3种方法:工厂模式.构造函数模式.原型模式.其实对于构造函数的概念,我们并不陌生.在之前学习c++语言的时候,也有提到过构造函数的概念.除了创建对象,构造函数(con ...

  3. ife task0003学习笔记(五):JavaScript面向对象

    JavaScript 支持函数式编程.闭包.基于原型的继承等高级功能.在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象.而在 JavaScript 中,this ...

  4. ife task0003学习笔记(二):JavaScript原型

    function aaa(){} aaa.prototype.bbb=function(){} var obj1=new aaa() var obj2=new aaa() obj1和obj2都有一个属 ...

  5. 微信小程序开发:学习笔记[5]——JavaScript脚本

    微信小程序开发:学习笔记[5]——JavaScript脚本 快速开始 介绍 小程序的主要开发语言是 JavaScript ,开发者使用 JavaScript 来开发业务逻辑以及调用小程序的 API 来 ...

  6. RX学习笔记:JavaScript数组操作

    RX学习笔记:JavaScript数组操作 2016-07-03 增删元素 unshift() 在数组开关添加元素 array.unshift("value"); array.un ...

  7. seaJs学习笔记之javascript的依赖问题

    之前分别为大家介绍了有关javascript中的冲突和性能问题,今天为大家介绍一下有关javascript中的依赖问题.我们将继续就之前javascript中性能问题继续介绍. 先来回顾一下性能问题的 ...

  8. 学习笔记:Javascript 变量 包装对象

    学习笔记:Javascript 变量 包装对象 如下代码,可以输出字符的长度. var str = "Tony"; str.length; 这时再试试以下代码,返回是 undefi ...

  9. 【学习笔记】JavaScript的基础学习

    [学习笔记]JavaScript的基础学习 一 变量 1 变量命名规则 Camel 标记法 首字母是小写的,接下来的字母都以大写字符开头.例如: var myTestValue = 0, mySeco ...

随机推荐

  1. log4net工作原理(2)

    上回说道:Repository可以说成基于一个log4net配置节创建的log4net容器,它根据log4net配置节的指示创建其他所有对象(Logger/Appender/Filter/Layout ...

  2. windows phone之依赖属性(DependencyProperty)

    Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统.由 WPF ...

  3. SQL Server Profiler (SQl跟踪器)的简单使用

    一.工具介绍 在实际开发中,我们的数据库应用系统因为不可避免会存在有大量表,视图,索引,触发器,函数,存储过程,sql语句等,所以会出现一系列问题,有问题不可怕,找对工具很重要.接下来我就介绍一款性能 ...

  4. 「BZOJ 3123」「SDOI 2013」森林「启发式合并」

    题意 你有一个森林,你需要支持两个操作 查询两个结点路径上权值第\(k\)小 两个点之间连一条边 强制在线,结点数\(\leq 8\times 10^4\) 题解 如果可以离线,这就是一个主席树板子题 ...

  5. kali linux之主动信息收集(三层发现,四层发现)

    三层发现: 比二层发现的优点即可路由,就是速度比二层慢,相对我们来说还是算快的,经常被边界防火墙过滤 ip icmp协议 OSI七层模型

  6. 洛谷P2762 太空飞行计划问题(最小割)

    传送门 我们可以把实验放在左边,仪器放在右边,点有点权,然后连对应的有向边,就是求一个最大权闭合图,可以转化为最小割来做(关于这具体是个啥……可以百度胡伯涛<最小割模型在信息学竞赛中的应用> ...

  7. Struts、Hibernate和Spring的整合

    Spring整合Hibernate Spring以其开放性,能与大部分ORM框架良好的整合.这样Spring就能轻松地使用ORM. Spring提供了DAO支持,DA0组件是应用的持久层访问的重要组件 ...

  8. Eclipse的一下设置

    一.设置自动补全 1.打开 Eclipse -> Window(窗口) -> Perferences(首选项) 2.点开java->Editor(编辑器)->Content A ...

  9. js事件处理程序return false ,preventDefault,returnValue

    面试题目中,经常会被问到如何阻止默认行为. 以下是<javascript权威指南>书中的内容,详情可以去看书. 能够取消事件默认操作的方法有三种 1.属性注册的事件处理程序的返回值fals ...

  10. 分布式中为什么要加入redis缓存的理解

    面我们介绍了mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存.那么什么是分布式缓存呢?假设现在有两个服务器1和2,用户访问的时候访问了1服务器,查询后的缓存就会放在1服务器 ...