apply的第一个参数thisarg决定函数内部this指向,非严格模式下null/undefined转为全局对象、原始值被包装,严格模式下保持原值;第二个参数argsarray必须是数组或类数组,会被展开为独立实参,且调用主体必须是函数。

apply 的第一个参数:thisArg 的绑定逻辑
apply 的第一个参数决定函数内部 this 的指向。它不是简单地“赋值”,而是经历一套规范定义的绑定流程。
在非严格模式下:
- 传入 null 或 undefined,this 会被自动替换为全局对象(浏览器中是
window,Node.js 中是global) - 传入原始值(如数字
42、布尔值true),会被自动包装成对应构造函数的实例(new Number(42)、new Boolean(true)) - 传入对象(包括数组、函数、普通对象),this 直接指向该对象
在严格模式下:
- null 和 undefined 不再被转换,this 就保持为 null 或 undefined
- 原始值也不会被包装,this 就是那个原始值本身(但多数情况下会报错,因原始值无属性可访问)
apply 的第二个参数:argsArray 的类型与展开规则
第二个参数必须是“类数组对象”或真正的数组,否则会抛出 TypeError。它不是被直接传进去,而是被逐项展开为独立实参。
- 数组(
[1, 2, 3])会被展开为fn(1, 2, 3) - 类数组对象(如
{0: 'a', 1: 'b', length: 2})只要具备length属性和数字键,也能被正确展开 -
arguments对象天然符合类数组结构,常用于参数转发:function proxy() { return realFn.apply(this, arguments); } - 传入 null 或 undefined,等价于不传任何参数,即
fn()
为什么 Math.max.apply(null, arr) 可行,而 Math.apply(arr) 报错?
关键在于调用主体必须是函数。Math 是一个内置对象,不是函数,不能直接调用;Math.max 才是函数。
-
Math.max.apply(null, [1,3,8,2]):合法。调用的是Math.max这个函数,apply 改变其 this(此处无关紧要),并展开数组为参数 -
Math.apply(null, [1,3,8,2]):非法。Math 不是函数,没有 apply 方法,会报TypeError: Math.apply is not a function - 同理,
Array.prototype.push.apply(arr1, arr2)可行,因为push是函数;但Array.apply(...)无效,因 Array 构造器虽可调用,但 apply 是 Function.prototype 上的方法,只属于函数对象
参数展开时的隐式行为与边界情况
apply 在展开 argsArray 时并不校验参数个数或类型,而是完全交由目标函数处理。这带来灵活性,也埋下隐患。
- 若数组长度超出引擎限制(如 V8 约 65536 个参数),会触发
RangeError - 若 argsArray 的
length属性为非整数或负数,展开结果不可预测(通常截断或忽略) - 稀疏数组(如
[1, , 3])中空位会被展开为undefined,而非跳过 - Symbol 键或非数字键(如
{'a': 1, length: 1})不会参与展开,仅数字索引有效











