第二章词法作用域

2.1 词法阶段

function foo(a) {
  var b = a * 2;
  function bar(c) {
    console.log( a, b, c );
  } 
  bar( b * 3 );
} 
foo( 2 ); // 2, 4, 12
  1. 包含着整个全局作用域, 其中只有一个标识符: foo。

  2. 包含着 foo 所创建的作用域, 其中有三个标识符: a、 bar 和 b。

  3. 包含着 bar 所创建的作用域, 其中只有一个标识符: c。

没有任何函数的气泡可以( 部分地) 同时出现在两个外部作用域的气泡
中, 就如同没有任何函数可以部分地同时出现在两个父级函数中一样

查找

作用域查找会在找到第一个匹配的标识符时停止

全局变量会自动成为全局对象( 比如浏览器中的 window 对象) 的属性, 因此
可以不直接通过全局对象的词法名称, 而是间接地通过对全局对象属性的引
用来对其进行访问。

window.a

通过这种技术可以访问那些被同名变量所遮蔽的全局变量。 但非全局的变量
如果被遮蔽了, 无论如何都无法被访问到。

2.2 欺骗词法

欺骗词法作用域会导致性能下降。

2.2.1 eval

function foo(str, a) {
  eval( str ); // 欺骗!
  console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3

在严格模式的程序中, eval(..) 在运行时有其自己的词法作用域, 意味着其
中的声明无法修改所在的作用域

JavaScript 中 还 有 其 他 一 些 功 能 效 果 和 eval(..) 很 相 似。 setTimeout(..) 和
setInterval(..) 的第一个参数可以是字符串, 字符串的内容可以被解释为一段动态生成的
函数代码。 这些功能已经过时且并不被提倡。 不要使用它们!

new Function(..) 函数的行为也很类似, 最后一个参数可以接受代码字符串, 并将其转
化为动态生成的函数( 前面的参数是这个新生成的函数的形参)。 这种构建函数的语法比
eval(..) 略微安全一些, 但也要尽量避免使用。

2.2.2 with

with 通常被当作重复引用同一个对象中的多个属性的快捷方式, 可以不需要重复引用对象
本身。

var obj = {
  a: 1,
  b: 2,
  c: 3
};
// 单调乏味的重复 "obj"
obj.a = 2;
obj.b = 3;
obj.c = 4;
// 简单的快捷方式
with (obj) {
  a = 3;
  b = 4;
  c = 5;
}

但实际上这不仅仅是为了方便地访问对象属性。 考虑如下代码:

function foo(obj) {
  with (obj) {
    a = 2;
  }
}
var o1 = {
  a: 3
};
var o2 = {
  b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好, a 被泄漏到全局作用域上了!

with 可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域, 因此这个对
象的属性也会被处理为定义在这个作用域中的词法标识符。

尽管 with 块可以将一个对象处理为词法作用域, 但是这个块内部正常的 var
声明并不会被限制在这个块的作用域中, 而是被添加到 with 所处的函数作
用域中。

o2 的作用域、 foo(..) 的作用域和全局作用域中都没有找到标识符 a, 因此当 a=2 执行
时, 自动创建了一个全局变量( 因为是非严格模式)。

另外一个不推荐使用 eval(..) 和 with 的原因是会被严格模式所影响( 限
制)。 with 被完全禁止, 而在保留核心功能的前提下, 间接或非安全地使用
eval(..) 也被禁止了。

2.2.3 性能

JavaScript 引擎会在编译阶段进行数项的性能优化。 其中有些优化依赖于能够根据代码的
词法进行静态分析, 并预先确定所有变量和函数的定义位置, 才能在执行过程中快速找到
标识符。

但如果引擎在代码中发现了 eval(..) 或 with, 它只能简单地假设关于标识符位置的判断
都是无效的, 因为无法在词法分析阶段明确知道 eval(..) 会接收到什么代码, 这些代码会
如何对作用域进行修改, 也无法知道传递给 with 用来创建新词法作用域的对象的内容到底
是什么。

2.3 小结

词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。 编译的词法分析阶段
基本能够知道全部标识符在哪里以及是如何声明的, 从而能够预测在执行过程中如何对它
们进行查找。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容