词法作用域中的eval()和with()

词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。嵌套的作用域类似为严格包含的嵌套气泡结构。

作用域查找从运行时所处的最内部作用域开始,逐级向外进行,直至遇见第一个匹配的标识符为止。

这种查找方式将引起“遮蔽效应”,即内部的标识符“遮蔽”了外部的标识符,被遮蔽的标识符除了全局变量
可以通过window.a的方式来访问,其余非全局的变量将无法被访问到。(全局变量会自动转换为window对象的属性)

存在两种机制可以打破这种规则:

eval()

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

在以上的代码块中,调用foo函数时对str传的参数是 var b = 3字符串,eval函数将会将这一段字符串转化为真实的有效代码,因而将原本会打印1,2的打印函数改写成了1,3 ,即原本foo函数作用域中不存在b的定义,在打印时将会查询到全局作用域下的b变量,但eval函数欺骗了作用域,在执行时在函数作用域中强行创建了一个b变量从而遮蔽了全局变量b。

严格模式中eval()有其自己的词法作用域,因而将会引发Reference异常,

with()

with方法可以使对同一个对象的多次访问变得便捷

var obj = {
	a:1,
	b,2,
	c:3
};
//若要对obj中的属性进行修改,则需要逐个访问
obj.a = 2;
obj.b = 3;
obj.c = 4;

//使用with方法
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被泄露到了全局作用域上了

第一次调用foo函数,对o1内部的a做了改动

第二次调用foo函数,o2内部不存在a属性,于是作用域查询上级作用域即全局作用域,也不存在,于是创建了一个a变量存储2

两次对a的赋值都是LHS查询,第一次找到了盒子于是对盒子里的值进行修改,第二次没有查询到于是创建了这个盒子并赋值。

不推荐使用eval()和with()函数,因为在javascript引擎在编译阶段会进行许多性能优化,期中一些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。

但代码中出现eval和with函数后,引擎无法在词法分析阶段知道eval将接收什么代码,也无法知道传递给with中的对象的作用域内的内容是什么,于是将会使得js引擎放弃许多优化(因为所有的优化可能将是无意义的),所有代码中大量使用eval和with将会使得代码运行变得非常慢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天长高了没1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值