《你不知道的JavaScript》--词法作用域(02)

一、词法阶段

词法作用域,就是定义在词法阶段的作用域,也是你再写代码时将变量和块作用域写在哪里来决定的。

看下如下代码

function foo(a){
  var b = a * 2
  function bar (c){
    console.log(a,b,c)
  }
  bar(b*3)
}
foo(2)
// 2,4 ,12

可以将它们想象成几个逐级包含的气泡


image.png

1.包含着整个作用域,其中只有一个标识符:foo
2.包含着foo做创建的作用域,其中有三个标识符:a,bar,b
3.包含着bar所创建的作用域,其中只有一个标识符c

在这个代码片段中,引擎执行console.log(),并查找a,b,c三个变量的引用,它首先从最内部的作用域bar中开始查找,找到了c,没有其他的就去上一层foo中继续查找,找了a,b,如果a,c都存在与bar和foo中,那他会直接使用bar中的变量,无需再去foo中查找

总之,作用域查找始终从运行时所需的最内部作用域开始,逐级向上进行,直到遇见第一个匹配的标识符为止。

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

无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都治由函数声明时所处的位置决定

词法作用域只会查找以及标识符,比如a, b, 如果代码中引用了foo.bar.baz,词法作用域查找只会视图查找foo,找到这个变量后,对象属性访问规则会分别接管对bar,baz属性的访问。

二、欺骗词法

当然,凡事无绝对,在运行时也可以‘修改’作用域。

在JavaScript中有两种机制来实现这个目的,这两种都会导致性能下降。见过的次数不多,做了解使用。

eval()

eval()函数接受一个字符串为参数,并将其中的内容视为好像在书写时就写在这个位置一样。

在执行eval()之后的代码时,引擎并不知道前面的代码是以动态的形式插入进来,并对词法作用域进行修改的,引擎只会如往常一样进行词法作用域查找。

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

eval()中的 var b = 3这段代码会被当做本来就在那里一样来处理,由于那段代码声明了一个新的变量b,因此他对foo的词法作用域进行了修改,所以当console.log执行的时候,会在foo内部同时找到a,b,因此会输出1,3,而不是成情况下输出的1,2

在严格模式中,eval()在运行时有自己的词法作用域,意味着其中的声明无法修改所在的作用域,所以上段代码在严格作用域下回输出1,2

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

width

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){
  width (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被泄漏到全局作用域上了

这个例子中创建了o1,o2两个对象,其中一个具有a属性,另一个没有。foo函数接受一个obj参数,该参数是一个对象引用,并对这个对象引用执行了width(obj){}。

当我们将o1传递进去,a=2赋值操作找到了o1.a并将2赋值给它,而当o2传递进去,o2并没有a属性,因此不会创建这个属性,o2.a保持undefined,但是a=2却执行了LHS引用(查看《你不知道的JavaScript》-作用域是什么(01)),创建了一个全局变量a,并将2赋值给它(非严格模式)。

可以这样理解,当我们传递o1给with时,with所声明的作用域是o1,而这个作用域中含有一个同o1.a属性相符的标识符。但当我们将o2作为作用域时,o2的作用域,foo的作用域,全局作用域都没有找到标识符a,因此进行了正常的LHS标识符查找,自动创建了一个全局变量(非严格模式)。

性能

eval(..)和with会在运行时修改或创建新的作用域,以此来欺骗其他在书写时定义的词法作用域。

你可能会问,那又怎样呢?如果它们能实现更复杂的功能,并且代码更具有扩展性,难道不是非常好的功能吗?答案是否定的。

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

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

如果代码中大量使用eval(..)或with,那么运行起来一定会变得非常慢。无论引擎多聪明,试图将这些悲观情况的副作用限制在最小范围内,也无法避免如果没有这些优化,代码会运行得更慢这个事实。

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

推荐阅读更多精彩内容