aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/reference/functions/arguments/callee/index.html
blob: c448f3b091da02770c75eb971f06cfac5126e4c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
---
title: arguments.callee
slug: Web/JavaScript/Reference/Functions/arguments/callee
tags:
  - Deprecated
  - JavaScript
  - arguments
  - arguments.callee
  - 函数
  - 属性
  - 已弃用
translation_of: Web/JavaScript/Reference/Functions/arguments/callee
---
<div>{{jsSidebar("Functions")}}</div>

<p><code><strong>arguments.callee </strong></code>属性包含当前正在执行的函数。</p>

<h2 id="Description">描述</h2>

<p><strong><code>callee</code></strong> 是 <code>arguments</code> 对象的一个属性。它可以用于引用该函数的函数体内当前正在执行的函数。这在函数的名称是未知时很有用,例如在没有名称的函数表达式 (也称为“匿名函数”)内。</p>

<div class="warning"><p><strong>警告:</strong><a href="/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode" title="/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode">严格模式</a>下,第5版 ECMAScript (<strong>ES5</strong>) 禁止使用 <code>a<strong>rguments.callee()</strong>。当一个函数必须调用自身的时候, 避免使用 <strong>arguments.callee(), </strong></code>通过<code>要么</code>给函数表达式一个名字,要么使用一个函数声明.</p></div>

<h2 id="为什么_arguments.callee_从ES5严格模式中删除了?">为什么 arguments.callee 从ES5严格模式中删除了?</h2>

<p>(改编自 <a href="http://stackoverflow.com/a/235760/578288" title="http://stackoverflow.com/a/235760/578288">a Stack Overflow answer by olliej</a>)</p>

<p>早期版本的 JavaScript不允许使用命名函数表达式,出于这样的原因, 你不能创建一个递归函数表达式。</p>

<p>例如,下边这个语法就是行的通的:</p>

<pre class="brush: js">function factorial (n) {
    return !(n &gt; 1) ? 1 : factorial(n - 1) * n;
}

[1,2,3,4,5].map(factorial);</pre>

<p>但是:</p>

<pre class="brush: js">[1,2,3,4,5].map(function (n) {
    return !(n &gt; 1) ? 1 : /* what goes here? */ (n - 1) * n;
});</pre>

<p>这个不行。为了解决这个问题, <code>arguments.callee</code> 添加进来了。然后你可以这么做</p>

<pre class="brush: js">[1,2,3,4,5].map(function (n) {
    return !(n &gt; 1) ? 1 : arguments.callee(n - 1) * n;
});</pre>

<p>然而,这实际上是一个非常糟糕的解决方案,因为这 (以及其它的 <code>arguments</code>, <code>callee</code>, 和 <code>caller</code> 问题) 使得在通常的情况(你可以通过调试一些个别例子去实现它,但即使最好的代码也是次优选项,因为(JavaScript 解释器)做了不必要的检查)不可能实现内联和尾递归。另外一个主要原因是递归调用会获取到一个不同的 <code>this</code> 值,例如:</p>

<pre class="brush: js">var global = this;

var sillyFunction = function (recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        alert("This is: " + this);
    } else {
        alert("This is the global");
    }
}

sillyFunction();</pre>

<p>ECMAScript 3 通过允许命名函数表达式解决这些问题。例如:</p>

<pre class="brush: js">[1,2,3,4,5].map(function factorial (n) {
    return !(n &gt; 1) ? 1 : factorial(n-1)*n;
});</pre>

<p>这有很多好处:</p>

<ul>
 <li>该函数可以像代码内部的任何其他函数一样被调用</li>
 <li>它不会在外部作用域中创建一个变量 (<a href="http://kangax.github.io/nfe/#example_1_function_expression_identifier_leaks_into_an_enclosing_scope">除了 IE 8 及以下</a>)</li>
 <li>它具有比访问arguments对象更好的性能</li>
</ul>

<p>另外一个被废弃的特性是 <code>arguments.callee.caller</code>,具体点说则是 <code>Function.caller。为什么</code>? 额,在任何一个时间点,你能在堆栈中找到任何函数的最深层的调用者,也正如我在上面提到的,在调用堆栈有一个单一重大影响:不可能做大量的优化,或者有更多更多的困难。比如,如果你不能保证一个函数 f 不会调用一个未知函数,它就绝不可能是内联函数 f。基本上这意味着内联代码中积累了大量防卫代码:</p>

<pre class="brush: js">function f (a, b, c, d, e) { return a ? b * c : d * e; }</pre>

<p>如果 JavaScript 解释器不能保证所有提供的参数数量在被调用的时候都存在,那么它需要在行内代码插入检查,或者不能内联这个函数。现在在这个特殊例子里一个智能的解释器应该能重排检查而更优,并检查任何将不用到的值。然而在许多的情况里那是不可能的,也因此它不能够内联。 </p>

<h2 id="Example.3A_Using_arguments.callee_in_an_anonymous_recursive_function">例子</h2>

<h3 id="Example.3A_Using_arguments.callee_in_an_anonymous_recursive_function">在匿名递归函数中使用 <code>arguments.callee</code></h3>

<p>递归函数必须能够引用它本身。很典型的,函数通过自己的名字调用自己。然而,匿名函数 (通过 <a href="/en-US/docs/JavaScript/Reference/Operators/function" title="JavaScript/Reference/Operators/Special/function">函数表达式</a> 或者 <code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function" title="JavaScript/Reference/Global_Objects/Function">函数构造器 </a>创建</code>) 没有名称。因此如果没有可访问的变量指向该函数,唯一能引用它的方式就是通过 <code>arguments.callee</code></p>

<p>下面的例子定义了一个函数,按流程,定义并返回了一个阶乘函数。该例并不是很实用,并且几乎都能够用 <a href="/en-US/docs/JavaScript/Reference/Operators/function" title="JavaScript/Reference/Operators/Special/function">命名函数表达式</a> 实现同样结果的例子, and there are nearly no cases where the same result cannot be achieved with .</p>

<pre class="brush: js">function create() {
   return function(n) {
      if (n &lt;= 1)
         return 1;
      return n * arguments.callee(n - 1);
   };
}

var result = create()(5); // returns 120 (5 * 4 * 3 * 2 * 1)
</pre>

<h3 id="没有替代方案的_arguments.callee">没有替代方案的 arguments.callee</h3>

<p>当你必须要使用Function构造函数时,下面的例子是没有可以替代 <code>arguments.callee</code> 的方案的,因此弃用它时会产生一个BUG (参看 {{Bug("725398")}}):</p>

<pre class="brush: js">function createPerson (sIdentity) {
    var oPerson = new Function("alert(arguments.callee.identity);");
    oPerson.identity = sIdentity;
    return oPerson;
}

var john = createPerson("John Smith");

john();</pre>

<p>译者注:利用命名函数表达式也可以实现上述例子的同样效果</p>

<pre class="brush: js">function createPerson (identity) {
    function Person() {
        console.log(Person.identity);
    }
    Person.identity = identity;
    return Person;
}
var john = createPerson("John Smith");

john(); //John Smith
</pre>

<h2 id="Specifications">Specifications</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>{{SpecName('ES1')}}</td>
   <td>{{Spec2('ES1')}}</td>
   <td>Initial definition. Implemented in JavaScript 1.2</td>
  </tr>
  <tr>
   <td>{{SpecName('ES5.1', '#sec-10.6', 'Arguments Object')}}</td>
   <td>{{Spec2('ES5.1')}}</td>
   <td> </td>
  </tr>
  <tr>
   <td>{{SpecName('ES6', '#sec-arguments-exotic-objects', 'Arguments Exotic Objects')}}</td>
   <td>{{Spec2('ES6')}}</td>
   <td> <br>
     </td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-arguments-exotic-objects', 'Arguments Exotic Objects')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td> </td>
  </tr>
 </tbody>
</table>

<h2 id="浏览器支持">浏览器支持</h2>



<p>{{Compat("javascript.functions.arguments.callee")}}</p>

<h2 id="也可以看看">也可以看看</h2>

<ul>
 <li>{{jsxref("Function")}}</li>
</ul>

<p> </p>