JavaScript欺骗词法的eval、with与catch及其性能问题
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了JavaScript欺骗词法的eval、with与catch及其性能问题,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4253字,纯文字阅读大概需要7分钟。
内容图文
![JavaScript欺骗词法的eval、with与catch及其性能问题](/upload/InfoBanner/zyjiaocheng/303/ae01e83097d749e5b56069e83ae36cd4.jpg)
正常来说,执行期上下文的作用域链是不会改变的
JavaScript中的词法作用域并不是一成不变的
(词法作用域/静态作用域: 作用域由书写代码时函数声明位置决定)
有几种机制是可以欺骗词法的
它们是with()、eval()还有try-catch语句的catch子句
其中with和eval我们不应该去使用(会产生很多问题)
欺骗词法的意思就是欺骗词法作用域
也就是说,它们在运行时改变了作用域链
下面我就来谈谈这些可以欺骗词法的机制
eval
eval()函数接受一个字符串作为参数,并且解析字符串生成代码
var a = 123;eval('console.log(a)');// 123
于是控制台打印了123
执行了eval函数之后
js引擎并不知道这段代码时动态插入的,并且修改了作用域链
引擎还会像往常一样查找作用域链
看下面的代码
var a = 1;function demo(){ eval('var a = 2;');//欺骗词法 console.log(a);// 2} demo();
当eval函数执行时,在demo函数执行环境的最顶端作用域添加了变量a
这个局部环境中的a“遮蔽”了全局环境的a
最终导致程序打印2
eval()函数不仅可以修改它当前所处的作用域,甚至还可以修改全局作用域
无论怎样,它都可以在运行期修改词法作用域
ES5的严格模式对这个函数加了一些限制,我把上面的代码加上局部严格模式
var a = 1;function demo(){ 'use strict'; eval('var a = 2;'); console.log(a);// 1} demo();
我们发现这回控制台打印了1
这是因为在严格模式下,eval()运行时拥有自己独立的词法作用域(省的它给执行环境的作用域链捣乱)
这样其中的声明就无法修改它所在的作用域了
这种可以动态产生代码的还有两个和它很像
定时器setTimeout()和setInterval()第一个参数可以是代码字符串
还有函数构造器new Function()的最后一个参数同样接受代码字符串
和eval()一样,不要使用这种用法,这会带来严重的性能问题,这个问题一会儿再说
with
另一个不建议使用的欺骗词法的语法就是这个with关键字
with通常用作重复引用某个对象的多个属性的快捷方式
好处是可以不需要重复的引用对象本身
比如我想重复使用console对象
console.log(1);console.info(2);console.warn(3);
使用with关键字
with(console){ log(1); info(2); warn(3); }
看起来with好像没什么问题,但是看下面
function demo(obj){ with(obj){ a = 5; } }var obj1 = {a:1};var obj2 = {b:2}; demo(obj1); console.log(obj1.a);// 5demo(obj2); console.log(obj2.a);// undefinedconsole.log(a);//5 -->变量a居然泄漏到了全局环境
我们发现使用with关键字修改了obj1的a
但是它不仅没有在obj2上增加a,反而产生副作用泄露到了全局
这是因为with可以把一个对象处理为一个完全隔离的词法作用域(放到作用域链的最前面)
所以在它内部产生执行a = 5;
它会向下查找作用域链,但没有找到,于是在全局创建了一个a变量(没有var 的声明)
注意:虽然with产生了一个词法作用域,但是with内部的正常var声明不会被限制在这个块作用域中
也就是说声明在with外部的作用域
像这样
function demo(){ var obj = {}; with(obj){ var b = 1; console.log(b); // 1 } console.log(b); // 1} demo(); console.log(b);// Uncaught ReferenceError: b is not defined
而且with关键字在ES5的严格模式干脆就不让用
如果你尝试使用你会看到这样的错误:
catch
除了eval与with之外,try-catch语句中的catch子句同样可以修改执行环境的作用域链
当try代码块内发生错误,执行流立即跳转到catch子句
随后把异常对象推入一个可变对象并且放到作用域链最前面,这和with很像
一旦catch子句执行完毕,作用域链就会恢复原样
但是和eval和with不同,try-catch还是相对有用,不用完全抛弃(虽然我没用过)
性能
欺骗词法会产生性能问题
js引擎在编译阶段会进行性能优化,很多优化依赖于能够根据代码词法进行静态分析
预先确定了变量和函数的定义位置,才能快速找到标识符
但是eval或with无法判断标识符位置(存在于代码执行过程中,无法静态分析)
也就是说:在eval和with面前,js引擎所有的优化没有任何意义(简直酷炫)
既然没意义,js引擎干脆就不优化了
这样就导致程序运行变慢了
对于with,它还有自己独特的性能问题…
产生了作用域,就会导致它所在的函数的所有局部变量处于第二个作用链对象
访问代价更高了
对于try-catch语句,如果我们想要使用,可以这样做
try{ ...}catch(e){ handleError(e); }
在catch语句中只执行了一段代码,委托给一个函数用于处理错误
这样没有局部变量的访问
作用域链的临时改变就不会影响性能
总结
总结一下重点
词法作用域意味着作用域是书写代码时函数声明的位置来决定
编译时词法分析阶段能知道所有标识符在哪里及如何声明eval可以对代码字符串进行演算,借此在运行时修改了词法作用域
with通过将一个对象引用当作作用域来处理,借此在运行时创建了词法作用域
eval在严格模式下会产生独立词法作用域,无法修改所在作用域
with在严格模式下禁止使用
eval与with(还有catch)可以欺骗词法,在执行时修改作用域链
eval与with致使js引擎无法在编译阶段优化作用域查找(无法静态分析),导致程序变慢
说了这么多,就是要告诉大家不要使用with关键字和eval函数~( ̄0 ̄)/
以上就是JavaScript欺骗词法的eval、with与catch及其性能问题的内容,更多相关内容请关注PHP中文网(www.gxlcms.com)!
内容总结
以上是互联网集市为您收集整理的JavaScript欺骗词法的eval、with与catch及其性能问题全部内容,希望文章能够帮你解决JavaScript欺骗词法的eval、with与catch及其性能问题所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。