资讯专栏INFORMATION COLUMN

【JS. ES5重点笔记】执行环境和作用域

Jeffrrey / 3051人阅读

摘要:作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。这样,一直延续到全局执行环境全局执行环境的变量对象始终都是作用域链中的最后一个对象。如果在局部环境中没有找到该变量名,则继续沿作用域链向上搜索。

【JavaScript.ES5】执行环境和作用域

参考文献:

Nicholas C.Zakas 《JavaScript》高级程序设计

仅为个人学习参考文献的内容记录的笔记。存在部分直接拿来的成分。不得不说,这本书写得很透彻。

引言和基本概念

执行环境是JS中很重要的概念,其定义了变量或者函数访问其他数据的权限,决定了其各自的行为。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。 ECMAScript 程序中的执行流正是由这个方便的机制控制着。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

标识符解是沿着作用域链一级一级往下拓展的,而搜素则是从作用域链的前端开始逐级回溯,直到找到标识符为止(找不到的话当然就报错了)。

下面请看简单的事例:

var color = "blue";

function changeColor(){
    if (color === "blue"){
        color = "red";
    } else {
        color = "blue";
        }
    }

changeColor();

alert("Color is now " + color);

通过运行可以发现,存行结果是红色,运行changecolor时,会去搜索color的值,在函数体内,未发现color的定义,于是会往外找,结果找到了color="blue"的定义,于是就会访问,获取,修改。

var color = "blue";

function changeColor(){
    var anotherColor = "red";

    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        
        //color, anotherColor, and tempColor are all accessible here
    }

    //color and anotherColor are accessible here, but not tempColor        
    swapColors();
}

changeColor();

//anotherColor and tempColor aren"t accessible here, but color is
alert("Color is now " + color);

上面涉及了3个执行环境,全局、changecolor()和swapColors三种。其中全局变量是color和anotherColor。changeColor()的局部环境中有一个名为anotherColor 的变量和一个名为swapColors()的函数,但它也可以访问全局环境中的变量 color。 swapColors()的局部环境中有一个变量tempColor,该变量只能在这个环境中访问到。无论全局环境还是 changeColor()的局部环境都无权访问 tempColor。然而,在 swapColors()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。

延长作用域链

下列两种情况可以延长作用域链:

try-catch语句的catch语句块

with语句

这两个语句都会在作用域链的前端添加一个变量对象。对 with 语句来说,会将指定的对象添加到作用域链中。对 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。

function buildUrl() {
    var qs = "?debug=true";

    with(location){
        var url = href + qs;        
    }

    return url;
}

var result = buildUrl();
alert(result);

with接受了location对象,于是变量对象就是location的所有属性和方法,变量对象就被添加在作用域链前端。buildUrl()函数中有局部变量qs。with引用变量href时,即location.href,能够被找到。引用变量qs,指的则是buildUrl()函数内部的qs。至于 with 语句内部,则定义了一个名为 url 的变量,因而 url 就成了函数执行环境的一部分,所以可以作为函数的值被返回。

没有块级的作用域

JS中并没有块级作用域。在类C语言中是有的,如条件语句,循环语句之类,大括号内属于一种作用域,但是在JS中并没有。

if (true) {
var color = "blue";
}

alert(color); //"blue"

显然,color在条件语句的大括号中,var定义的也是局部变量,但实际上,大括号外面能访问到想访问的color。

声明变量

使用 var 声明的变量会自动被添加到最接近的环境中。而且仅限于该环境。反之,如果该声明没有带,var会被自动认为是全局变量。

function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}

var result = add(10, 20); //30
alert(sum);

这里报错了,因为sum在add函数中为局部变量,从大括号外面并不能访问到sum。如果改成下面这样,就能正常运行。

function add(num1, num2) {
    sum = num1 + num2;
    return sum;
}

var result = add(10, 20); //30
alert(sum); //30
查询标识符

当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿作用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中也没有找到这个标识符,则意味着该变量尚未声明,会报错。

var color = "blue";

function getColor(){
    return color;
}

alert(getColor()); //"blue"

alert中调用了getcolor()函数,这个函数中访问了color,然而函数中并没有声明color,于是会向上找color,在全局中找到了,于是就取值。

var color = "blue";

function getColor(){
    var color = "red";
    return color;
}

alert(getColor()); //"red"
alert(color);//"blue"

getcolor函数体内有color的声明,而且是局部的,所以并不会覆盖全局的blue。在getcolor内部,color就是内部声明的color,一旦搜索到定义之后就不会再搜索,所以就是red;而外面的color则并不会因为里面的color是red而改变,所以还是blue。

补充: 变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快,因为不用向上搜索作用域链。 JavaScript 引擎在优化标识符查询方面做得不错,因此这个差别在将来恐怕就可以忽略不计了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82517.html

相关文章

  • 《ECMAScript 6 入门》读书笔记

    摘要:阮一峰老师开源作品。书上的示例代码可以通过在线网站代码调试工具调试。 阮一峰老师开源作品。 书上的示例代码可以通过 在线网站代码调试工具 JS Bin 调试。 作用域 作用域链 每个变量或函数通过执行环境 (execution context) 定义了其有权访问的其他数据,决定了他们各自的行为; 全局执行环境是最顶层的执行环境,根据宿主环境的不同,表示全局执行环境的对象也不同:在浏览...

    qieangel2013 评论0 收藏0
  • 《你不知道的javascript》笔记_作用与闭包

    摘要:建筑的顶层代表全局作用域。实际的块级作用域远不止如此块级作用域函数作用域早期盛行的立即执行函数就是为了形成块级作用域,不污染全局。这便是闭包的特点吧经典面试题下面的代码输出内容答案个如何处理能够输出闭包方式方式下一篇你不知道的笔记 下一篇:《你不知道的javascript》笔记_this 写在前面 这一系列的笔记是在《javascript高级程序设计》读书笔记系列的升华版本,旨在将零碎...

    galaxy_robot 评论0 收藏0
  • JavaScript 语言核心笔记(持续更新)

    摘要:在同一个块内,不允许用重复声明变量。中为新增了块级作用域。自带遍历器的对象有数组字符串类数组对象对象的对象等和结构对象。返回一个遍历器,使遍历数组的键值对键名键值。 目录 1.语法 2.类型、值和变量 3.表达式和运算符 4.语句 5.数组 6.对象 7.函数 8.全局属性和方法 9.词法作用域、作用域链、闭包 10.原型链、继承机制 11.this的理解 12.ES5新特性 13.E...

    suosuopuo 评论0 收藏0
  • [译] ES6 学习笔记:关于 ES2015 特性的详细概述

    摘要:将转换成常见的使用实现的基于迭代器的迭代。处停止迭代器基于鸭子模型接口这里使用语法仅仅为了说明问题使用支持为了使用迭代器属性需要引入。生成器是迭代器的子类,包含了附加的与。 原文地址:http://babeljs.io/docs/learn-...本文基于Luke Hoban精妙的文章《es6features》,请把star献给他,你可以在此尝试这些特性REPL。 概述 ECMAScr...

    leoperfect 评论0 收藏0
  • 【进阶1-1期】理解JavaScript 中的执行上下文执行

    摘要:首次运行代码时,会创建一个全局执行上下文并到当前的执行栈中。执行上下文的创建执行上下文分两个阶段创建创建阶段执行阶段创建阶段确定的值,也被称为。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,,今天是第一天 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进...

    import. 评论0 收藏0

发表评论

0条评论

Jeffrrey

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<