在学习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. UltraEdit 回车符替换空格

    查找和替换    输入 ^r^n   替换为:(空格)

  2. ExposedObject的使用

    ExposedObject可以将一个对象快速封装未一个dynamic using System; namespace ConsoleApp2 { class Program { static void ...

  3. orleans 2.0 教程之-----官方文档翻译,给大家学习ol一个参考

    本人也是英文盲,翻译不对的地方请谅解.由于翻译内容较多,会慢慢更新 orleans简称ol,一些专用词不做翻译.先决条件,读这表文章之前需要了解:actor,es,cqrs 参考链接: https:/ ...

  4. 玩转php缓存memcache

    记录 一.本地安装 链接:https://www.cnblogs.com/jkko123/p/6294669.html 二.linux安装

  5. JSP页面导出PDF格式文件

    JSP页面导出PDF格式文件基本在前端页面可以全部完成 <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/ ...

  6. luoguP4782 [模板]2-SAT问题

    https://www.luogu.org/problemnew/show/P4782 2-SAT模板,输出方案只需判断 \(a\) 和 \(a + n\) 两个点所在的 scc 编号大小就可以了 # ...

  7. 解决 Github用户名 变为 invalid-email-address 问题

    解决 Github用户名 变为 invalid-email-address 问题 If the identity used for this commit is wrong, you can fix ...

  8. (C/C++) Link List - C++ 版本

    利用C++寫一個基本的 Link list 練習,功能包含 pint list.CreatList.Insert.Delete.Reverse.Search.Clear.GetLen. 先建立相關的C ...

  9. 自旋锁Spin lock与互斥锁Mutex的区别

    POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API.线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用 ...

  10. Qt 学习之路 2(71):线程简介

    Qt 学习之路 2(71):线程简介 豆子 2013年11月18日 Qt 学习之路 2 30条评论 前面我们讨论了有关进程以及进程间通讯的相关问题,现在我们开始讨论线程.事实上,现代的程序中,使用线程 ...