理解 Javascript 中的作用域与作用域链

本贴最后更新于 2519 天前,其中的信息可能已经事过景迁

理解 Javascript 中的作用域与作用域链

最近恶补基础知识。非常感谢一像素博主的文章 JS 核心系列:浅谈函数的作用域,以及 JavaScript 开发进阶:理解 JavaScript 作用域和作用域链。这两篇文章对我的帮助非常大。

任何程序设计语言都有作用域,简单的说,作用域就是变量或函数的可访问范围。作用域控制着变量与函数的可访问性与生命周期。
在绝大部分程序设计语言中,作用域的范围有全局作用域、函数作用域、块级作用域。

  • 全局作用域:属于全局作用域的代码在任何地方都能访问到;
  • 函数作用域:在定义该变量/函数的函数体内可以访问到;
  • 块级作用域:在定义该变量/函数的语句块中可以访问到。

然而对于 Javascript(ES5)来说,不存在块级作用域。
例如以下语句块:

if(true) {
  var foo = "bar";
}
console.log(foo); // bar

while(1) {
  var fooo = "baar";
  break;
}
console.log(fooo); // baar

当然,在 es6 中可以使用 let 关键字定义变量,使变量拥有块级作用域

while(1) {
  let foooo = "baaar";
  break;
}
console.log(foooo); // Uncaught ReferenceError: foooo is not defined;

js 的函数作用域,具体可以体现在下例

function func1() {
  var x = 1;
  console.log(x);
}
func1();
console.log(x); // Uncaught ReferenceError: x is not defined;

在作用域内的变量没有被 var 声明,则自动提升为全局作用域。

function func2() {
  y = 1;
  console.log(y);
}
func2();
console.log(y); // 1

因为 js 存在“变量提升”的特性,所以在未使用严格模式('use strict')的情况下,在某一作用域内定义的变量可以在任何位置被调用而不出错,哪怕是在其被声明之前。

console.log(upper);  // undefined
var upper = 3;

当某对象被调用时,js 会依此按照
当前作用域 -> 次级作用域 -> 次次级作用域 -> ... -> 全局作用域
来查找并使用变量。此即为作用域链。

a = 1;
function add() {
  var b = 3;
  function in1() {
    var a = 2;
    console.log(a + b); // 5
  }
  function in2() {
    console.log(a + b); // 4
  }
  in1();
  in2();
}
add();

in1() 中,作用域链为 in1 -> add -> window,所以 a + b 此时的 ain1 内定义的 a,值为 2
in2() 中,作用域链为 in2 -> add -> window,所以 a + b 此时的 awindow 内定义的 a,值为 1

在未使用严格模式时,可以使用 with 关键字临时扩展作用域。

a = 1;
function add() {
  var b = 3;
  function in1() {
    var a = 2;
    console.log(a + b); // 5
  }
  function in2(obj) {
    with(obj) {
      console.log(a + b); // 12
    }
  }
  var obj = {a: 9};
  in1();
  in2(obj);
}
add();

in2() 中,因为 with 关键字的原因,with 内的对象被添加到作用域链的头部,形成了如下作用域链:
obj -> in2 -> add -> window
因此,此时 in2with 语句块内的 a 值为 9

以上,可以看出,在运行期上下文中,访问全局变量的速度是最慢的。因为全局作用域总是处在作用域链的最末端。
所以,在编写代码的时候,尽可能少使用全局变量,多使用局部变量。
有前人总结了经验,是:如果有一个变量跨作用域引用了一次以上,则先将它存储到局部变量里再使用。

在面试中经常讲 this 和作用域混合在一起出题。所以这里也说说 this
在一个函数中(es5),this 总是指向调用该函数的对象,总是在运行时才能确定 this 的值以及其指向。

var obj = {
  x: 1,
  log: function() {
    console.log(this.x);
  }
};

obj.log(); // 1
var func3 = obj.log;
func3(); // undefined

直接调用函数的话,函数体内的 this 默认指向 window

window.name = "func4";
function func4() {
  console.log(this.name);
}
func4(); // func4;

使用 call, bind, apply 可以动态改变函数体内 this 的指向。使用 new 关键字调用函数也会使函数体内的 this 指向发生变化。这些留给以后说。

一像素的博客内有两道题,我这边照搬过来。这两道题是对作用域、this 最好的锻炼题。

// code1
var foo = "window";
var obj = {
    foo : "obj",
    getFoo : function(){
       return function(){
            return this.foo;
        };
    }
};
var f = obj.getFoo();
f(); // window
 // code2
var foo = "window";
var obj = {
    foo : "obj",
    getFoo : function(){
       var that = this;
        return function(){
            return that.foo;
        };
    }
};
var f = obj.getFoo();
f(); // obj

以上所有测试代码,都可以在我的 github 内下载并调试。

  • 研究
    12 引用 • 34 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1347 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    710 引用 • 1173 回帖 • 192 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
zjhch123
未来的事无人知晓,所以才有无穷可能。 杭州

推荐标签 标签

  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 7 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 18 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 430 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    66 引用 • 124 回帖
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖
  • OnlyOffice
    4 引用 • 19 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 1 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖 • 1 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 346 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖 • 3 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 41 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 636 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 247 回帖 • 212 关注
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    81 引用 • 122 回帖 • 614 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    163 引用 • 1446 回帖 • 1 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 1 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    3 引用 • 74 回帖 • 3 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    116 引用 • 99 回帖 • 275 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 56 关注
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    90 引用 • 383 回帖
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 294 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 8 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 426 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖