前端面试题:如何使字符串可执行

来源:互联网 发布:apache启动报错 编辑:程序博客网 时间:2024/06/06 10:40

这是一道今年腾讯春招的面试题。当考官一开始问我这道题的时候,我没有反应过来,什么叫使字符串可执行。好吧,请原谅我的无知。考官耐心的解释,字符串可执行的意思是将字符串转为立即执行的代码,如字符串var a=23;,使其经过某种操作后确实有个值为23的a变量。听了面试官的话后,我第一时间想到了eval方法。然而面试官并不满意,追问我还有其他方式吗,我一时没有想起来。面试过后我就想总结一下这个问题,不过因为拖延症,拖到今天才写,敬请见谅。本文内容主要借鉴《JavaScript忍者秘籍》中使字符串转为立即执行的代码一节。下面将讲述字符串可执行的四种方式。

1. eval()方法

eval()方法是一个很强大的方法,它接收一个字符串作为参数,当解析器发现代码中调用该方法时,会将传入的参数当作ECMAScript语句来解析,然后把执行结果插入到原位置。如代码eval('var str="hi"');,执行了这个语句后,就可以直接使用变量str了,如console.log(str)将输出”hi”。不过关于这个方法有以下几点要注意:

(1). 通过该方法执行的代码有与该执行环境相同的作用域链。这意味着它可以引用在包含环境中定义的变量。也意味着它可以形成闭包,这与下文的Function方法形成对比。

var str="hi, I'm student!";eval('console.log(str)');//output:hi, I'm student!

(2). 在eval()中创建的任何变量或函数都不会被提升。这很好理解,因为它只有在解析之后才会运行。

f1()    //Uncaught ReferenceError: f1 is not definedeval("function f1(){console.log('hi')}")

(3). 任何不是简单变量、原始值、赋值语句的内容都需要在外面包装一个括号。执行返回结果则是最后一个表达式的执行结果。eval("{name:1}")的结果是1,惊不惊喜,意不意外?

var o1=eval("1");console.log(o1);//output:1var o2=eval("{name:1}")console.log(o2);//output:1    why?var o3=eval("({name:1})")console.log(o3);//output: Object {name: 1}eval("3+4,5+6")//output:11

(4). eval()方法是全局方法,但eval和window.eval结果有时不同的。前者作用域顶端是当前上下文,后者作用域是window对象。

var color='red';function test(){  var color='blue';  eval('console.log(color)');  window.eval('console.log(color)');  with(window){    eval('console.log(color)');  }}test()//outputs://blue//red//red

2. 使用Function构造器

我们都知道函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。函数除了使用函数声明和函数表达式,第三种定义函数的方式就是使用Function构造函数。以下摘自《JavaScript高级程序设计》:

Function构造函数可以接受任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。
我们不推荐读者使用这种方法定义函数,因为这种语法会导致解析两次代码(第一次是解析常规的ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。

我相信大家平常也很少使用这种方式,不过这种方式在模板引擎中(如EJS)应用很广泛。使用它确实能使字符串执行,只不过作用域是在函数里。但有一点要注意,就是它不会创建闭包,请看如下代码:

var color="red"function test1(){  var color="blue";  eval("var f1=function(){console.log(color)}");  return f1;}test1()()//output:bluefunction test2(){  var color="blue";  var f2=new Function("console.log(color)");  return f2;}test2()()//output:red       no closure!

3. 使用setTimeout

setTimeout通常被用来执行异步代码,如可以用setTimeout(function(){console.log(2)},1000)来实现隔1s后输出2。不过你也可以在第一个参数中传入字符串,如下所示:

function print(num){    console.log(num);}setTimeout("print(2)",1000);

当传入的第一个参数为字符串时,感觉像是在使用eval方法处理似的,至于是不是,我也不知道。如果您知道,请不吝赐教。其实setTimeout很强大,我也收集过一篇讲述setTimeout的好文章,就是它了:你应该知道的setTimeout秘密。

4. 巧用<script>标签

将要执行的代码放在动态生成的script标签內,并将标签注入文档中,同样可实现字符串的执行。如下所示:

var str="console.log(2222)"var script=document.createElement('script');script.text=str;document.body.appendChild(script);document.body.removeChild(script);//output:2222

由于本人才疏学浅,暂时只知道这四种,如果您还有其他方法,欢迎指教。如您发现错误,请不吝指正。

原创粉丝点击