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

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

理解 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 回帖 • 1 关注
  • JavaScript

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

    710 引用 • 1173 回帖 • 176 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 导航

    各种网址链接、内容导航。

    37 引用 • 168 回帖
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1425 引用 • 10043 回帖 • 471 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 7 关注
  • 安全

    安全永远都不是一个小问题。

    189 引用 • 813 回帖 • 1 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 10 关注
  • App

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

    90 引用 • 383 回帖 • 1 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 404 关注
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 22 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    6515 引用 • 29287 回帖 • 247 关注
  • 强迫症

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

    15 引用 • 161 回帖 • 1 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 97 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    5 引用 • 15 回帖 • 223 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 512 关注
  • 七牛云

    七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化 PaaS 服务。围绕富媒体场景,七牛先后推出了对象存储,融合 CDN 加速,数据通用处理,内容反垃圾服务,以及直播云服务等。

    25 引用 • 215 回帖 • 163 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖 • 1 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖 • 1 关注
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    85 引用 • 895 回帖
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 5 关注
  • 博客

    记录并分享人生的经历。

    270 引用 • 2386 回帖 • 1 关注
  • 996
    13 引用 • 200 回帖 • 1 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    523 引用 • 4581 回帖 • 692 关注
  • Gitea

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

    4 引用 • 16 回帖 • 3 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    7 引用 • 26 回帖 • 3 关注
  • SQLite

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

    4 引用 • 7 回帖
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    103 引用 • 126 回帖 • 452 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 553 关注