2019-04-21 11:50:48 +08:00

224 lines
20 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<article id="wikiArticle">
<div>
<div>
<div></div>
<div><code><strong>eval()</strong></code><strong> </strong>函数会将传入的字符串当做 JavaScript 代码进行执行。</div>
<div> </div>
</div>
</div>
<h2 id="Syntax" name="Syntax">语法</h2>
<pre><code class="language-javascript"><code>eval(<em>string</em>)</code></code></pre>
<h3 id="Parameters" name="Parameters">参数</h3>
<dl>
<dt><code>string</code></dt>
<dd>表示JavaScript表达式语句或一系列语句的字符串。表达式可以包含变量以及已存在对象的属性。</dd>
</dl>
<h3 id="返回值">返回值</h3>
<p>执行指定代码之后的返回值。如果返回值为空,返回<a href="Reference/Global_Objects/undefined" title="undefined是全局对象的一个属性。也就是说它是全局作用域的一个变量。undefined的最初值就是原始数据类型undefined。"><code>undefined</code></a></p>
<h2 id="Description" name="Description">描述</h2>
<p><code>eval()</code> 是全局对象的一个函数属性。</p>
<p><code>eval()</code> 的参数是一个字符串。如果字符串表示的是表达式,<span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code> </span>会对表达式进行求值。如果参数表示一个或多个 JavaScript 语句, 那么 <span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code></span>会执行这些语句。注意不要用 <span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code> </span>来执行一个算术表达式因为 JavaScript 可以自动为算术表达式求值。</p>
<p>如果你以字符串的形式构造算术表达式,则可以用 <span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code> </span>在随后对其求值。例如,假设你有一个变量 <code>x</code> ,您可以通过将表达式的字符串值(例如 <code>3 * x + 2</code> )赋值给一个变量,然后在你的代码后面的其他地方调用 <code>eval()</code> ,来推迟涉及 <code>x</code> 的表达式的求值。</p>
<p><font face="Open Sans, Arial, sans-serif">如果 <code>eval()</code> 的参数不是字符串, </font><span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code> </span>将会将参数原封不动的返回。在下面的例子中,<code>String</code> 构造器被指定, 而 <span style="font-family: consolas,monaco,andale mono,monospace;"><code>eval()</code> </span>返回了 <code>String</code> 对象而不是执行字符串。</p>
<pre><code class="language-javascript">eval(new String("2 + 2")); // 返回了包含"2 + 2"的字符串对象
eval("2 + 2"); // returns 4
</code></pre>
<p>你可以使用通用的的方法来绕过这个限制,如使用<code>toString()</code></p>
<pre><code class="language-javascript">var expression = new String("2 + 2");
eval(expression.toString());
</code></pre>
<p>如果你间接的使用 <span style="font-family: consolas,monaco,andale mono,monospace;">eval(),比如</span>通过一个引用来调用它,而不是直接的调用 <code>eval</code> 。 从 <a class="external" href="http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2" rel="noopener">ECMAScript 5</a> 它工作在全局作用域下而不是局部作用域中。这就意味着例如下面的代码的作用声明创建一个全局函数并且geval中的这些代码在执行期间不能在被调用的作用域中访问局部变量。</p>
<pre><code class="language-javascript">function test() {
var x = 2, y = 4;
console.log(eval("x + y")); // 直接调用,使用本地作用域,结果是 6
var geval = eval; // 等价于在全局作用域调用
console.log(geval("x + y")); // 间接调用使用全局作用域throws ReferenceError 因为`x`未定义
<code> (0, eval)('x + y'); // 另一间接调用的例子
</code>}</code></pre>
<h2 id="Don.27t_use_eval.21" name="Don.27t_use_eval.21">避免在不必要的情况下使用 <code>eval</code></h2>
<p><code>eval()</code> 是一个危险的函数, 他执行的代码拥有着执行者的权利。如果你用 <code>eval()</code> 运行的字符串代码被恶意方(不怀好意的人)操控修改,您最终可能会在您的网页/扩展程序的权限下在用户计算机上运行恶意代码。更重要的是第三方代码可以看到某一个eval()被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 <a href="Reference/Global_Objects/Function" title="Function 构造函数 创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个Function对象。"><code>Function</code></a> 就不容易被攻击。</p>
<p><code>eval()</code> 通常比替代方法慢,因为它必须调用 JS 解释器,而许多其他结构则由现代 JS 引擎进行优化。</p>
<p>在常见的案例中我们都会找更安全或者更快的方案去替换 <code>eval()</code></p>
<h3 id="访问成员属性">访问成员属性</h3>
<p>你不应该去使用 <code>eval()</code> 来将属性名字转化为属性。考虑下面的这个例子,被访问对象的属性在它被执行之前都会未知的。这里可以用 eval 处理</p>
<pre><code class="language-javascript">var obj = { a: 20, b: 30 };
var propName = getPropName(); <code>// 返回 "a" 或 "b"</code>
eval( 'var result = obj.' + propsName )</code></pre>
<p>但是,这里并不是必须得使用 <code>eval()</code> 。事实上,这里并不建议这样使用。可以使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors" title="JavaScript/Reference/Operators/Member_Operators">属性访问器</a> 进行代替,它更快而且更安全:</p>
<pre><code class="language-javascript">var obj = { a: 20, b: 30 }
var propName = getPropName();<code> // 返回 "a" 或 "b"</code>
var result = obj[ propName ]; <code>// obj[ "a" ] 与 obj.a 等价</code></code></pre>
<p>你还可以使用这个方法去访问子代的属性。如下:</p>
<pre><code class="language-javascript">var obj = {a: {b: {c: 0}}};
var propPath = getPropPath(); // 例如返回 "a.b.c"
eval( 'var result = obj.' + propPath )
</code></pre>
<p>在这里避免 <code>eval()</code> 可以通过分割属性路径和循环遍历不同的属性来完成:</p>
<pre><code class="language-javascript">function getDescendantantProp(obj, desc) {
var arr = desc.split('.');
while(arr.length) {
obj = obj[arr.shift()];
}
return obj;
}
var obj = {a: {b: {c: 0}}};
var propPath = getPropPath(); // 例如返回 "a.b.c"
var result = getDescendantantProp(obj, propPath);</code></pre>
<p>同样的方法也可实现设置子代的属性值:</p>
<pre><code class="language-javascript">function setDescendantProp(obj, desc, value) {
var arr = desc.split('.');
while (arr.length &gt; 1) {
obj = obj[arr.shift()];
}
obj[arr[0]] = value;
}
var obj = {a: {b: {c: 0}}};
var propPath = getPropPath(); // 例如返回 "a.b.c"
var result = setDescendantProp(obj, propPath, 1); // a.b.c 值为 1</code></pre>
<h3 id="使用函数而非代码段">使用函数而非代码段</h3>
<p>JavaScript拥有 <a href="/zh-CN/docs/Glossary/First-class_Function" title="http://en.wikipedia.org/wiki/First-class_function">first-class functions</a>这意味着你可以将函数直接作为参数传递给其他接口将他们保存在变量中或者对象的属性中等等。很多DOM的API都用这种思路进行设计你也可以或者应该这样子设计你的代码</p>
<pre><code class="language-javascript">// 代替 setTimeout(" ... ", 1000) 写法:
setTimeout(function() { ... }, 1000);
// 代替 elt.setAttribute("onclick", "...") 写法:
elt.addEventListener('click', function() { ... } , false);</code></pre>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures" title="JavaScript/Guide/Closures">闭包</a> 也有助于创建参数化函数而不用连接字符串。</p>
<h3 id="Parsing_JSON" name="Parsing_JSON">解析 JSON将字符串转化为JavaScript对象</h3>
<p>如果你在调用 <code>eval()</code> 传入的字符串参数中包含数据(如:一个数组“[1,2,3]”)而不是代码,你应该考虑将其转换为 <a href="https://developer.mozilla.org/en-US/docs/Glossary/JSON">JSON</a> 对象这允许你用JavaScript语法的子集来表示数据。<a href="/zh-CN/docs/Downloading_JSON_and_JavaScript_in_extensions">在扩展中下载JSON和JavaScript</a></p>
<p>提示:因为 JSON 语法子集相对于 JavaScript 语法子集比较有局限性,很多在 JavaScript 中可用的特性在 JSON 中就不起作用了。比如,后缀逗号在 JSON 中不支持,并且对象中的属性名在 JSON 中必须用引号括起来。请务必使用 JSON 序列化方法来生成稍后将被解析为 JSON 的字符串。</p>
<h3 id="尽量传递数据而非代码">尽量传递数据而非代码</h3>
<p>例如设计为抓取网页内容的扩展可能会在XPath中定义抓取规则而不是在 JavaScript 代码中。</p>
<h3 id="以有限权限运行代码">以有限权限运行代码</h3>
<p>如果你必须执行这段代码, 应考虑以更低的权限运行。此建议主要适用于扩展和XUL应用程序可以使用<a href="https://developer.mozilla.org/en-US/docs/Components.utils.evalInSandbox" title="Components.utils.evalInSandbox">Components.utils.evalInSandbox</a> 。</p>
<h2 id="Examples" name="Examples">例子</h2>
<h3 id="使用_eval">使用 <code>eval</code></h3>
<p>在下面的代码中,两种包含了 <code>eval()</code> 的声明都返回了42。第一种是对字符串 "<code>x + y + 1</code>" 求值;第二种是对字符串 "<code>42</code>"求值。</p>
<pre><code class="language-javascript">var x = 2;
var y = 39;
var z = "42";
eval("x + y + 1"); // returns 42
eval(z); // returns 42
</code></pre>
<h3 id="Example:_Using_eval_to_evaluate_a_string_of_JavaScript_statements" name="Example:_Using_eval_to_evaluate_a_string_of_JavaScript_statements">使用 <code>eval</code> 执行一串 JavaScript 语句</h3>
<p>下面的例子使用 <code>eval()</code> 来执行 <code>str</code> 字符串。这个字符串包含了如果 <code>x</code> 等于5就打开一个Alert 对话框并对 <code>z</code> 赋值 42否则就对 <code>z</code> 赋值 0 的 JavaScript 语句。 当第二个声明被执行,<code>eval()</code> 将会令字符串被执行,并最终返回赋值给 <code>z</code> 的 42。</p>
<pre><code class="language-javascript">var x = 5;
var str = "if (x == 5) {alert('z is 42'); z = 42;} else z = 0; ";
<code>console.log('z is ', eval(str));</code></code></pre>
<p>如果您定义了多个值,则会返回最后一个值。</p>
<pre><code class="language-javascript"><code>var x = 5;
var str = "if (x == 5) {console.log('z is 42'); z = 42; x = 420; } else z = 0;";
console.log('x is ', eval(str)); // z is 42 x is 420</code>
</code></pre>
<h3 id="Return_value" name="Return_value">返回值</h3>
<p><code>eval</code> 返回最后一个表达式的值。</p>
<pre><code class="language-javascript">var str = "if ( a ) { 1+1; } else { 1+2; }";
var a = true;
var b = eval(str); // returns 2
<code>console.log('b is : ' + b);</code>
a = false;
b = eval(str); // returns 3
<code>console.log('b is : ' + b);</code>
</code></pre>
<h3 id="eval_中函数作为字符串被定义需要“”和“”作为前缀和后缀"><code>eval</code> 中函数作为字符串被定义需要“(”和“)”作为前缀和后缀</h3>
<pre><code>var fctStr1 = 'function a() {}'
var fctStr2 = '(function a() {})'
var fct1 = eval(fctStr1) // return undefined
var fct2 = eval(fctStr2) // return a function</code>
</code></pre>
<h2 id="规范">规范</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td><a class="external" href="https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%201st%20edition,%20June%201997.pdf" hreflang="en" lang="en" rel="noopener" title="ECMAScript 1st Edition (ECMA-262)">ECMAScript 1st Edition (ECMA-262)</a></td>
<td><span class="spec-Standard">Standard</span></td>
<td>Initial definition.</td>
</tr>
<tr>
<td><a class="external" href="https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1" hreflang="en" lang="en" rel="noopener">ECMAScript 5.1 (ECMA-262)<br/><small lang="zh-CN">eval</small></a></td>
<td><span class="spec-Standard">Standard</span></td>
<td> </td>
</tr>
<tr>
<td><a class="external" href="https://www.ecma-international.org/ecma-262/6.0/#sec-eval-x" hreflang="en" lang="en" rel="noopener">ECMAScript 2015 (6th Edition, ECMA-262)<br/><small lang="zh-CN">eval</small></a></td>
<td><span class="spec-Standard">Standard</span></td>
<td> </td>
</tr>
<tr>
<td><a class="external" href="https://tc39.github.io/ecma262/#sec-eval-x" hreflang="en" lang="en" rel="noopener">ECMAScript Latest Draft (ECMA-262)<br/><small lang="zh-CN">eval</small></a></td>
<td><span class="spec-Draft">Draft</span></td>
<td> </td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<p></p><div class="bc-data"><a class="bc-github-link external" href="https://github.com/mdn/browser-compat-data" rel="noopener">Update compatibility data on GitHub</a><table class="bc-table bc-table-js"><thead><tr class="bc-platforms"><td></td><th class="bc-platform-desktop" colspan="6"><span>Desktop</span></th><th class="bc-platform-mobile" colspan="7"><span>Mobile</span></th><th class="bc-platform-server" colspan="1"><span>Server</span></th></tr><tr class="bc-browsers"><td></td><th class="bc-browser-chrome"><span class="bc-head-txt-label bc-head-icon-chrome">Chrome</span></th><th class="bc-browser-edge"><span class="bc-head-txt-label bc-head-icon-edge">Edge</span></th><th class="bc-browser-firefox"><span class="bc-head-txt-label bc-head-icon-firefox">Firefox</span></th><th class="bc-browser-ie"><span class="bc-head-txt-label bc-head-icon-ie">Internet Explorer</span></th><th class="bc-browser-opera"><span class="bc-head-txt-label bc-head-icon-opera">Opera</span></th><th class="bc-browser-safari"><span class="bc-head-txt-label bc-head-icon-safari">Safari</span></th><th class="bc-browser-webview_android"><span class="bc-head-txt-label bc-head-icon-webview_android">Android webview</span></th><th class="bc-browser-chrome_android"><span class="bc-head-txt-label bc-head-icon-chrome_android">Chrome for Android</span></th><th class="bc-browser-edge_mobile"><span class="bc-head-txt-label bc-head-icon-edge_mobile">Edge Mobile</span></th><th class="bc-browser-firefox_android"><span class="bc-head-txt-label bc-head-icon-firefox_android">Firefox for Android</span></th><th class="bc-browser-opera_android"><span class="bc-head-txt-label bc-head-icon-opera_android">Opera for Android</span></th><th class="bc-browser-safari_ios"><span class="bc-head-txt-label bc-head-icon-safari_ios">Safari on iOS</span></th><th class="bc-browser-samsunginternet_android"><span class="bc-head-txt-label bc-head-icon-samsunginternet_android">Samsung Internet</span></th><th class="bc-browser-nodejs"><span class="bc-head-txt-label bc-head-icon-nodejs">Node.js</span></th></tr></thead><tbody><tr><th scope="row"><code>eval</code></th><td class="bc-supports-yes bc-browser-chrome"><span class="bc-browser-name">Chrome</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-edge"><span class="bc-browser-name">Edge</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-firefox"><span class="bc-browser-name">Firefox</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
1</td><td class="bc-supports-yes bc-browser-ie"><span class="bc-browser-name">IE</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-opera"><span class="bc-browser-name">Opera</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-safari"><span class="bc-browser-name">Safari</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-webview_android"><span class="bc-browser-name">WebView Android</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-chrome_android"><span class="bc-browser-name">Chrome Android</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-edge_mobile"><span class="bc-browser-name">Edge Mobile</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-firefox_android"><span class="bc-browser-name">Firefox Android</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
4</td><td class="bc-supports-yes bc-browser-opera_android"><span class="bc-browser-name">Opera Android</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-safari_ios"><span class="bc-browser-name">Safari iOS</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-samsunginternet_android"><span class="bc-browser-name">Samsung Internet Android</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td><td class="bc-supports-yes bc-browser-nodejs"><span class="bc-browser-name">nodejs</span><abbr class="bc-level-yes only-icon" title="Full support">
<span>Full support</span>
</abbr>
Yes</td></tr></tbody></table><section class="bc-legend" id="sect1"><h3 class="offscreen" id="Legend">Legend</h3><dl><dt><span class="bc-supports-yes bc-supports">
<abbr class="bc-level bc-level-yes only-icon" title="Full support">
<span>Full support</span>
 
</abbr></span></dt><dd>Full support</dd></dl></section></div><p></p>
<h2 id="火狐相关">火狐相关</h2>
<ul>
<li>从历史上看,<code>eval()</code> 有一个可选的第二个参数,指定上下文执行对象。 这个参数是非标准的,并且明确地从 Firefox 4 中删除。请参阅 <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=531675" rel="noopener" title="FIXED: Ignore the second argument of eval()">bug 531675</a></li>
</ul>
<h2 id="See_also" name="See_also">其他内容</h2>
<ul>
<li><a href="Reference/Global_Objects/uneval" title="uneval() 函数创建一个代表对象的源代码的字符串。"><code>uneval()</code></a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors">Property accessors</a></li>
<li><a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Using_eval()_in_content_scripts">WebExtensions: Using eval in content scripts</a></li>
</ul>
</article>