aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/reference/global_objects/function/apply/index.html
blob: 2db7f42b1d149cb8c602e3b16de2202ecb87b787 (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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
---
title: Function.prototype.apply()
slug: Web/JavaScript/Reference/Global_Objects/Function/apply
tags:
  - Function
  - JavaScript
  - Method
translation_of: Web/JavaScript/Reference/Global_Objects/Function/apply
---
<div>{{JSRef}}</div>

<p><strong><code>apply()</code></strong> 方法调用一个具有给定<code>this</code>值的函数,以及以一个数组(或<a href="/zh-CN/docs/Web/JavaScript/Guide/Indexed_collections#Working_with_array-like_objects">类数组对象</a>)的形式提供的参数。</p>

<div class="note">
<p><strong>备注:</strong>call()方法的作用和 apply() 方法类似,区别就是<code>call()</code>方法接受的是<strong>参数列表</strong>,而<code>apply()</code>方法接受的是<strong>一个参数数组</strong></p>
</div>

<div>{{EmbedInteractiveExample("pages/js/function-apply.html")}}</div>



<h2 id="Syntax">语法</h2>

<pre class="syntaxbox"><code><em>func</em>.apply(<em>thisArg</em><em>, [</em><em>argsArray</em>])</code></pre>

<h3 id="Parameters">参数</h3>

<dl>
 <dt><code>thisArg</code></dt>
 <dd>必选的。在 <em><code>func</code></em> 函数运行时使用的 <code>this</code> 值。请注意,<code>this</code>可能不是该方法看到的实际值:如果这个函数处于{{jsxref("Strict_mode", "非严格模式", "", 1)}}下,则指定为 <code>null</code> 或 <code>undefined</code> 时会自动替换为指向全局对象,原始值会被包装。</dd>
 <dt><code>argsArray</code></dt>
 <dd>可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 <code>func</code> 函数。如果该参数的值为 {{jsxref("null")}} 或  {{jsxref("undefined")}},则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。<a href="#浏览器兼容性">浏览器兼容性</a> 请参阅本文底部内容。</dd>
</dl>

<h3 id="返回值">返回值</h3>

<p>调用有指定<code><strong>this</strong></code>值和参数的函数的结果。</p>

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

<p>在调用一个存在的函数时,你可以为其指定一个 <code>this</code> 对象。 <code>this</code> 指当前对象,也就是正在调用这个函数的对象。 使用 <code>apply</code>, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。</p>

<p><code>apply</code> 与 {{jsxref("Function.call", "call()")}} 非常相似,不同之处在于提供参数的方式。<code>apply</code> 使用参数数组而不是一组参数列表。<code>apply</code> 可以使用数组字面量(array literal),如 <code>fun.apply(this, ['eat', 'bananas'])</code>,或数组对象, 如  <code>fun.apply(this, new Array('eat', 'bananas'))</code></p>

<p>你也可以使用 {{jsxref("Functions/arguments", "arguments")}}对象作为 <code>argsArray</code> 参数。 <code>arguments</code> 是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。</p>

<p>从 ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个 <code>length</code> 属性和<code>(0..length-1)</code>范围的整数属性。例如现在可以使用 {{domxref("NodeList")}} 或一个自己定义的类似 <code>{'length': 2, '0': 'eat', '1': 'bananas'}</code> 形式的对象。</p>

<div class="note">
<p><strong>备注:</strong> Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。</p>
</div>

<h2 id="Examples">示例</h2>

<h3 id="用_apply_将数组各项添加到另一个数组">用 <code>apply</code> 将数组各项添加到另一个数组</h3>

<p>我们可以使用<code>push</code>将元素追加到数组中。由于push接受可变数量的参数,所以也可以一次追加多个元素。</p>

<p>但是,如果<code>push</code>的参数是数组,它会将该数组作为单个元素添加,而不是将这个数组内的每个元素添加进去,因此我们最终会得到一个数组内的数组。如果不想这样呢?<code>concat</code>符合我们的需求,但它并不是将元素添加到现有数组,而是创建并返回一个新数组。 然而我们需要将元素追加到现有数组......那么怎么做好?难道要写一个循环吗?别当然不是!</p>

<p><code>apply</code>正派上用场!</p>

<pre class="brush: js">var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
</pre>

<h3 id="apply_and_built-in_functions">使用<code>apply</code>和内置函数</h3>

<p>对于一些需要写循环以遍历数组各项的需求,我们可以用<code>apply</code>完成以避免循环。</p>

<p>下面是示例,我们将用<code>Math.max</code>/<code>Math.min</code>求得数组中的最大/小值。</p>

<pre class="brush: js">/* 找出数组中最大/小的数字 */
var numbers = [5, 6, 2, 3, 7];

/* 使用Math.min/Math.max以及apply 函数时的代码 */
var max = Math.max.apply(null, numbers); /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
var min = Math.min.apply(null, numbers);

/* 对比:简单循环算法 */
max = -Infinity, min = +Infinity;

for (var i = 0; i &lt; numbers.length; i++) {
  if (numbers[i] &gt; max)
    max = numbers[i];
  if (numbers[i] &lt; min)
    min = numbers[i];
}</pre>

<p>注意:如果按上面方式调用<code>apply</code>,有超出JavaScript引擎参数长度上限的风险。一个方法传入过多参数(比如一万个)时的后果在不同JavaScript 引擎中表现不同。(JavaScriptCore引擎中有被硬编码的<a class="link-https" href="https://bugs.webkit.org/show_bug.cgi?id=80797"> 参数个数上限:65536</a>)。这是因为此限制(实际上也是任何用到超大栈空间的行为的自然表现)是不明确的。一些引擎会抛出异常,更糟糕的是其他引擎会直接限制传入到方法的参数个数,导致参数丢失。比如:假设某个引擎的方法参数上限为4(实际上限当然要高得多), 这种情况下,上面的代码执行后, 真正被传递到 <code>apply</code>的参数为 <code>5, 6, 2, 3</code> ,而不是完整的数组。</p>

<p>如果你的参数数组可能非常大,那么推荐使用下面这种混合策略:将数组切块后循环传入目标方法:</p>

<pre class="brush: js">function minOfArray(arr) {
  var min = Infinity;
  var QUANTUM = 32768;

  for (var i = 0, len = arr.length; i &lt; len; i += QUANTUM) {
    var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
    min = Math.min(submin, min);
  }

  return min;
}

var min = minOfArray([5, 6, 2, 3, 7]);
</pre>

<h3 id="Using_apply_to_chain_constructors">使用apply来链接构造器</h3>

<p>你可以使用apply来链接一个对象<a href="/zh-CN/docs/JavaScript/Reference/Operators/new" title="JavaScript/Reference/Operators/new">构造器</a>,类似于Java。在接下来的例子中我们会创建一个全局<a href="/zh-CN/docs/JavaScript/Reference/Global_Objects/Function" title="JavaScript/Reference/Global_Objects/Function"><code>Function</code></a> 对象的construct方法 ,来使你能够在构造器中使用一个类数组对象而非参数列表。</p>

<pre class="brush: js">Function.prototype.construct = function (aArgs) {
  var oNew = Object.create(this.prototype);
  this.apply(oNew, aArgs);
  return oNew;
};
</pre>

<div class="note">
<p><strong>备注:</strong> 上面使用的<code>Object.create()</code>方法相对来说比较新。另一种可选的方法,请考虑如下替代方法:</p>

<p>Using {{jsxref("Object/__proto__", "Object.__proto__")}}:</p>

<pre class="brush: js">Function.prototype.construct = function (aArgs) {
  var oNew = {};
  oNew.__proto__ = this.prototype;
  this.apply(oNew, aArgs);
  return oNew;
};</pre>

<p>使用闭包:</p>

<pre class="brush: js">Function.prototype.construct = function(aArgs) {
  var fConstructor = this, fNewConstr = function() {
    fConstructor.apply(this, aArgs);
  };
  fNewConstr.prototype = fConstructor.prototype;
  return new fNewConstr();
};</pre>

<p class="brush: js">使用 Function 构造器:</p>

<pre class="brush: js">Function.prototype.construct = function (aArgs) {
  var fNewConstr = new Function("");
  fNewConstr.prototype = this.prototype;
  var oNew = new fNewConstr();
  this.apply(oNew, aArgs);
  return oNew;
};</pre>
</div>

<p>使用示例:</p>

<pre class="brush: js">function MyConstructor (arguments) {
    for (var nProp = 0; nProp &lt; arguments.length; nProp++) {
        this["property" + nProp] = arguments[nProp];
    }
}

var myArray = [4, "Hello world!", false];
var myInstance = new MyConstructor(myArray); //Fix MyConstructor.construct is not a function

console.log(myInstance.property1);                // logs "Hello world!"
console.log(myInstance instanceof MyConstructor); // logs "true"
console.log(myInstance.constructor);              // logs "MyConstructor"
</pre>

<div class="note">
<p><strong>备注:</strong> 这个非native的<code>Function.construct</code>方法无法和一些native构造器(例如<a href="/zh-CN/docs/JavaScript/Reference/Global_Objects/Date" title="JavaScript/Reference/Global_Objects/Date"><code>Date</code></a>)一起使用。 在这种情况下你必须使用<a href="/zh-CN/docs/JavaScript/Reference/Global_Objects/Function/bind#Bound_functions_used_as_constructors" title="JavaScript/Reference/Global_Objects/Function/bind#Bound_functions_used_as_constructors"><code>Function.bind</code></a>方法(例如,想象有如下一个数组要用在Date构造器中: <code>[2012, 11, 4]</code>;这时你需要这样写: <code>new (Function.prototype.bind.apply(Date, [null].concat([2012, 11, 4])))()</code> – -无论如何这不是最好的实现方式并且也许不该用在任何生产环境中).</p>
</div>

<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>{{SpecName('ES3')}}</td>
   <td>{{Spec2('ES3')}}</td>
   <td>Initial definition. Implemented in JavaScript 1.3.</td>
  </tr>
  <tr>
   <td>{{SpecName('ES5.1', '#sec-15.3.4.3', 'Function.prototype.apply')}}</td>
   <td>{{Spec2('ES5.1')}}</td>
   <td></td>
  </tr>
  <tr>
   <td>{{SpecName('ES6', '#sec-function.prototype.apply', 'Function.prototype.apply')}}</td>
   <td>{{Spec2('ES6')}}</td>
   <td></td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-function.prototype.apply', 'Function.prototype.apply')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td></td>
  </tr>
 </tbody>
</table>

<h2 id="浏览器兼容性">浏览器兼容性</h2>

<p>{{Compat("javascript.builtins.Function.apply")}}</p>

<h2 id="See_also">相关链接</h2>

<ul>
 <li>{{jsxref("Functions_and_function_scope/arguments", "arguments ")}} object</li>
 <li>{{jsxref("Function.prototype.bind()")}}</li>
 <li>{{jsxref("Function.prototype.call()")}}</li>
 <li>{{jsxref("Functions", "Functions and function scope", "", 1)}}</li>
 <li>{{jsxref("Reflect.apply()")}}</li>
</ul>