欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

词法作用域 & 欺骗语法

程序员文章站 2022-07-15 10:40:11
...

词法作用域

  1. 词法作用域是定义在词法阶段的作用域,是在写代码时将变量和块作用域写在哪里来决定的。
  2. 无论函数在哪里/如何被调用,它的词法作用域都只由函数被声明时所处的位置来决定

欺骗词法

欺骗词法作用域会导致性能下降,不可使用,但是在此进行介绍其原理
1. eval:

  • eval(…)可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码并运行,就好像代码是写在那个位置的一样。
  • 在执行eval(…)之后的代码时,引擎并不知道前面的代码是以动态形式插入进来的,并对词法作用域的环境进行修改的,引擎只会如往常的进行词法作用域查找。

观察以下代码:

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

eval(…)调用中的“var b = 3;”这段代码会被当作本来就在那里一样来处理,由于这段代码声明了新的b,因此它对已经存在的foo(…)的此法作用域进行了修改,相当于在foo(…)内部创建了新变量b覆盖了外部(全局)作用域中的同名变量
当console.log被执行时,会在foo(…)的内部同时找到a和b,但是永远也无法找到外部的b,因此输出1 3,而不是2 3。
但是,在严格模式的程序中,eval(…)在运行时有自己的此法作用域,其中的声明无法修改所在的作用域。如下代码:

function foo(str) {
	"use strict";
	eval(str); // 欺骗
	console.log(a);// ReferenceError: a is not defined
}
foo("var a = 3;");

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

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;
}
  1. 性能
    eval和with会在运行时修改或创建新的作用域,以此来欺骗其他在书写时定义的词法作用域。如果引擎在代码中发现了二者,它只能简单地假设关于标识符位置的判断都是无效的,因为无法在词法分析阶段明确知道eval会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给with用来创建新词法作用域对象的内容到底是什么。而且,如果出现了eval和with,所有的优化可能都是无意义的,因此最简单的做法就是完全不做任何优化。如果代码中大量使用二者,运行起来会非常慢
相关标签: 作用域

上一篇: 枚举类

下一篇: 枚举类