call和apply用于显式指定函数执行时的this值并传参,核心差异在于参数传递方式:call逐个传参,apply以数组形式传参,常用于借用Array方法处理类数组对象。
javascript 中的 call 和 apply 是函数对象的两个内置方法,核心作用是**显式指定函数执行时的 this 值,并传入参数**。它们最典型的实用场景之一,就是“借用”其他对象的方法——尤其是把数组(array.prototype)上的方法,临时用在类数组对象(如 arguments、dom 节点列表)或普通对象上。
为什么需要借用数组方法?
很多对象“长得像数组”,有 length 属性和数字索引(例如函数内部的 arguments、document.querySelectorAll() 返回的 NodeList),但它们**不是真正的 Array 实例**,因此不能直接调用 push、slice、forEach 等数组方法。这时就需要从 Array.prototype 上“借”一个方法过来用。
call 与 apply 的基本用法差异
两者功能完全一致,区别只在**参数传递方式**:
-
func.call(thisArg, arg1, arg2, ...):参数逐个列出 -
func.apply(thisArg, [arg1, arg2, ...]):参数以数组(或类数组)形式传入
当你要把一个已有的数组作为参数列表传给目标方法时,apply 更方便;若参数明确且数量固定,call 更直观。
常见借用场景与写法
1. 把 arguments 转成真数组
ES5 中常用:var args = Array.prototype.slice.call(arguments);
等价于:var args = Array.prototype.slice.apply(arguments);
二者都让 slice 在 arguments 上执行,返回新数组。
2. 给类数组对象使用 forEach / map / filter
例如遍历 NodeList:
Array.prototype.forEach.call(document.querySelectorAll('div'), function(el) { console.log(el); });-
[].forEach.call(els, handler)是更简洁的写法(空数组字面量也是 Array 实例)
3. 借用 push 向类数组添加元素(需手动维护 length)
注意:类数组对象本身没有 push,但可以借用,前提是它有可写的 length 属性:
var arrLike = {0: 'a', 1: 'b', length: 2};Array.prototype.push.call(arrLike, 'c', 'd');- 执行后
arrLike变为{0:'a', 1:'b', 2:'c', 3:'d', length:4}
现代替代方案(ES6+)
虽然 call/apply 借用仍有效,但如今更推荐语义清晰、兼容性良好的新方式:
-
Array.from(nodeList)→ 转为数组 -
[...nodeList]→ 展开运算符(要求可迭代) -
Array.prototype.slice.call(arguments)→ 已被Array.from(arguments)或剩余参数function(...args) { }替代
不过理解 call/apply 的借用机制,对深入掌握 this 绑定和原型继承依然非常重要。










