词法作用域

是不是有时候我太理性了?


我的同事说这个猫的眼神像我。我要是如我猫这般可爱多好啊~

1. 词法阶段


大部分标准语言编译器的第一个工作阶段叫做词法化。词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词语义。

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

  • 作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识符,这叫做“遮蔽效应”。
  • 无论函数在哪里被调用,他的词法作用域只由函数被声明时所处的位置决定。
  • 词法作用域查找只会查找一级标识符。如果代码引用了foo.bar.baz,词法作用域查找只会试图查找foo标识符,找到变量后,对象属性访问规则会分别接管对bar和baz属性的访问。

2. 欺骗词法(修改词法作用域)


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

两种欺骗词法作用域的机制:

2.1 eval

eval()函数接受一个字符串为参数,并将其中的内容视为在书写时就存在于程序中这个位置的代码。

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

这段代码在foo()内部创建了一个变量b,遮蔽了外部的b。

在程序中动态生成代码的使用场景非常罕见,因为它带来的好处无法抵消性能上的损失。

2.2 with

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

比如:

var obj = {
  a : 1,
  b : 2,
  c : 3  
}
// 修改obj的值
obj.a = 2;
obj.b = 3;
obj.a = 4;
// 快捷方式
with(obj){
  a = 1;
  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 被泄漏到全局作用域上了!

当我们传递 o1 给 with 时,with 所声明的作用域是 o1,而这个作用域中含 有一个同 o1.a 属性相符的标识符。但当我们将 o2 作为作用域时,其中并没有 a 标识符, 因此进行了正常的 LHS 标识符查找

有个副作用就是a=2赋值操作创建了一个全局的变量a。

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

  • eval()函数如果接受了含有一个或多个声明的代码,就会修改其所处的词法作用域,而with声明实际上是根据你传递给它的对象凭空创建一个全新的词法作用域。
  • eval()和with会被严格模式所影响,with被完全禁止,在保留核心功能的前提下,间接或者非安全的使用eval()也被禁止

3. 性能


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

最悲观的情况是,如果出现eval()和with,所有的优化可能都是无意义的,因此最简单的做法就是不做任何优化。

so,不要使用他们!!!

愿我们有能力不向生活缴械投降---Lin

本文分享自微信公众号 - 女程序员的日常(gh_df41d619fb70),作者:凛

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 作用域是什么?

    程序需要一套规则来存储变量,并且之后可以方便的找到这些变量。这套规则就称为作用域。

    用户3258338
  • 用node.js和mock.js实现mock数据

    各位宝宝们,好久不见了,最近我没有安排好时间,so,把自己都给了工作,需要好好学习怎么管理时间~

    用户3258338
  • Promise

    Promise构造函数执行时立即调用executor函数,resolve和reject两个函数作为参数传递给executor(executor函数在Promis...

    用户3258338
  • JS学习系列 02 - 词法作用域

    “作用域”我们知道是一套规则,用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。

    liuxuan
  • HDFS之使用Java客户端对文件的一些操作

    爱学习的孙小白
  • 通俗讲解从Transformer到BERT模型!

    在学会 Transformer 和 Bert 之前,我们需要理解Attention和Self-Attention机制。Attention的本质是要找到输入的fe...

    Datawhale
  • matlab强化学习Sarsa与Sarsa(lambda)对比

    适用于回合型环境,要等到回合结束, 才开始对本回合所经历的所有步都添加更新, 但是这所有的步都是和宝藏有关系的, 都是为了得到宝藏需要学习的步, 所以每一步在下...

    万木逢春
  • 前端绘图:js-sequence-diagrams安装及入门

    1.js-sequence-diagrams的作用 将简单的文本行绘制成手绘风(或是简单的直线条)的流程图。优点是不需要复杂的数据结构。 ---- 这次做一个数...

    kalifa_lau
  • PHP网络技术(六)——session及与cookie的比较

    PHP网络技术(六) ——session及与cookie的比较 (原创内容,转载请注明来源,谢谢) 一、概念 session是持续的、双向性的...

    用户1327360
  • 有哪些网络安全漏洞存在?

    根据国家信息安全漏洞共享平台(China National Vulnerability Database,简称CNVD,)统计的本周信息安全漏洞威胁...

    墨者安全筱娜

扫码关注云+社区

领取腾讯云代金券