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

本文详细介绍了编译器的词法阶段,重点讲解了词法作用域的概念及其查找规则。词法作用域在定义时即已确定,通常不可更改,但在JavaScript中,eval和with可以欺骗词法作用域。eval可动态修改作用域,with则创建新的作用域层,但这两种方法可能导致性能下降和全局变量泄露,因此不推荐使用。理解词法作用域对于优化代码和避免意外的全局变量至关重要。

1 词法阶段

1.1 什么是词法阶段
  • 编译器的第一个工作阶段,即上一章中编译原理的第一步:将字符串拆分成有意义的词法单元的过程。
1.2 什么是词法作用域
  • 在词法阶段定义的作用域。也就是说,词法作用域仅由你在书写代码时将变量和块作用域写在哪有关,与它在哪被调用或如何调用无关。
1.3 词法作用域可以更改吗
  • 原则上来讲,变量和块作用域在定义时其词法作用域就已经生成了,大部分情况下是不可更改的。但事实上有一些欺骗词法作用域的方法,虽然保持词法作用域根据书写位置生成的自然关系不变是最佳实践。
1.4 在词法作用域中的查找
  • 词法作用域的查找会在第一次匹配到之后终止
  • 由当前词法作用域开始查找,依次向上查找
  • 当词法作用域产生嵌套时,允许创建同名变量,但内部作用域的变量会“屏蔽”外部同名变量。重要的是,除非外部同名变量为全局变量,否则一旦产生“屏蔽”,外部同名变量在内部将无法访问得到
  • 词法作用域只会查找一级变量,eg:foo.bar.baz,只会查找一级变量foo,找到之后,对象属性的访问规则会分别接管对bar和baz属性的访问

2 欺骗词法

2.1 eval
  • eval()接受一个字符串作为参数,并将其中的内容视为好像在书写时就存在于程序那个位置的代码,eg:
function foo(str) {
    eval(str)
    console.log(b)
}
var b = 2
foo(var b = 3)  // b = 3,访问到的是foo作用域的局部变量b

但需要注意的是,eval在严格模式下是有自己的作用域的,eg:

function foo(str) {
    “use strict”
    eval(str)
    console.log(b)
}
foo(var b = 3)   // b is not defined
  • 与eval作用类似的还有setTimeout(…)和setInterval(…),它们的第一个参数都可以是字符串,原理同eval,但出于性能和安全性考虑,已经被淘汰,不要使用!
  • new Function(…) 的最后一个参数接受字符串,前面的参数是这个新生成函数的形参
2.2 with
  • with常被当作重复引用同一对象下不同属性的快捷方式,eg:
var obj = {
    a: 1,
    b: 2,
    c: 3
}
obj.a = 2
obj.b = 3
obj.c = 4
// 功能等价于
with(obj) {
    a = 3,
    b = 4,
    c = 5
}
  • 但with相当于在当前作用域下凭空创建了一个新的作用域,不再遵循对象的属性访问规则,而是将其看成是一个新的作用域下的标识符,遵循LHS或RHS查找规则,eg:
function foo(obj) {
    with(obj) {
        a = 2
    }
    console.log(a)      // 2
    console.log(obj)    // {b: 3}
    console.log(obj.a)  // undefined
}
var o2 = {
    b: 3
}
foo(o2)
console.log(o2.a)  // undefined,遵循对象的属性访问规则,obj内没有属性a,则隐式创建一个,但值为初始值
console.log(a)     // 2,o2中没有属性a,所以with中的a会执行LHS查询,导致全局泄漏,即在全局创建了变量a并赋值为2

eval(…)和with会严重影响JS的性能,不必要的时候不推荐使用

3 小结

  • eval(…)可以对一段包含一个或多个声明的“代码”字符串进行演算,并借此修改已经存在的词法作用域(在运行时)
  • with的本质是通过将一个对象的引用当作作用域来处理,将对象的属性当作作用域中的标识符来处理,从而创建了一个新的词法作用域(同样是在运行时)
  • 欺骗词法的副作用:引擎无法在编译时对作用域进行查找优化,因为它们不定地会改变编译完成后已经存在的作用域,只能默认为优化是无效的,不推荐使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值