← JavaScript调用函数 call() JavaScript函数 bind() →

JavaScript函数 apply()

原创 2026-03-12 JavaScript 已有人查阅

JavaScript apply()方法详解:数组参数的高效传递

什么是apply()方法?

apply()是JavaScript中函数对象的内置方法,它允许我们调用一个函数,并显式指定函数内部的this指向,同时以数组形式传递参数。

与call()方法的主要区别在于:apply()接受数组形式的参数,而call()接受参数列表

// 基本语法
functionName.apply(thisArg, [argumentsArray])

参数说明:

  • thisArg:可选参数,函数执行时this指向的值

  • argumentsArray:可选参数,类数组对象或数组,包含传递给函数的参数

apply()方法的核心用途

1. 使用Math.max找数组较大值

这是apply()最经典的应用场景之一:

let scores = [87, 95, 76, 89, 92, 68];

// 不使用apply()的写法
let maxScore = Math.max(scores[0], scores[1], scores[2], scores[3], scores[4], scores[5]);
console.log('较高分:', maxScore);  // 95

// 使用apply()的优雅写法
let maxScoreApply = Math.max.apply(null, scores);
console.log('较高分(apply):', maxScoreApply);  // 95

// 现在替代方案(ES6展开运算符)
let maxScoreModern = Math.max(...scores);
console.log('较高分(现在写法):', maxScoreModern);  // 95

2. 数组追加元素

将一个数组的所有元素追加到另一个数组:

let primaryColors = ['红色', '蓝色', '黄颜色'];
let secondaryColors = ['绿色', '紫色', '橙色'];

// 使用apply()追加数组
primaryColors.push.apply(primaryColors, secondaryColors);
console.log('合并后的颜色数组:', primaryColors);
// ['红色', '蓝色', '黄颜色', '绿色', '紫色', '橙色']

// 不同数据类型也可以混合
let numbers = [1, 2, 3];
let mixed = ['一', '二', '三'];
numbers.push.apply(numbers, mixed);
console.log('混合类型数组:', numbers);
// [1, 2, 3, '一', '二', '三']

个人经验:虽然现在有更简洁的展开运算符(...),但在处理一些需要兼容旧环境(如IE)的项目时,apply()仍然是可靠的方案。

apply()与call()的对比

function introduce(greeting, punctuation) {
    console.log(greeting + ',我叫' + this.name + punctuation);
}

let person = { name: '李华' };

// call()接受参数列表
introduce.call(person, '你好', '!');  // 你好,我叫李华!

// apply()接受参数数组
introduce.apply(person, ['你好', '!']);  // 你好,我叫李华!

选择建议:

  • 参数数量固定且较少时,用call()更直观

  • 参数已经在数组中时,用apply()更方便

实际应用场景

场景一:数组小值的灵活应用

function findMinInArray(arr) {
    return Math.min.apply(null, arr);
}

let temperatures = [23, 19, 25, 21, 18, 22, 24];
let minTemp = findMinInArray(temperatures);
console.log('本周较低温度:', minTemp, '°C');  // 18°C

// 处理嵌套数组
let nestedNumbers = [[5, 8], [3, 9], [7, 2]];
let allNumbers = [];

// 合并所有子数组
nestedNumbers.forEach(function(subArray) {
    allNumbers.push.apply(allNumbers, subArray);
});

console.log('扁平化后的数组:', allNumbers);  // [5, 8, 3, 9, 7, 2]
console.log('所有数字中的小值:', Math.min.apply(null, allNumbers));  // 2

场景二:构造函数链式调用

function Animal(species, sound) {
    this.species = species;
    this.sound = sound;
    this.makeSound = function() {
        console.log(this.species + '发出' + this.sound + '声');
    };
}

function Dog(name, color) {
    // 借用Animal构造函数
    Animal.apply(this, ['狗', '汪汪']);
    this.name = name;
    this.color = color;
}

function Cat(name, color) {
    // 借用Animal构造函数
    Animal.apply(this, ['猫', '喵喵']);
    this.name = name;
    this.color = color;
}

let myDog = new Dog('旺财', '棕色');
let myCat = new Cat('', '白色');

myDog.makeSound();  // 狗发出汪汪声
myCat.makeSound();  // 猫发出喵喵声
console.log(myDog.name, myDog.color);  // 旺财 棕色

场景三:处理类数组对象

function processArguments() {
    // 将类数组对象arguments转换为真正的数组
    let args = Array.prototype.slice.apply(arguments);
    console.log('参数数组:', args);
    
    // 或者使用apply直接调用数组方法
    let sum = 0;
    Array.prototype.forEach.apply(arguments, [function(num) {
        sum += num;
    }]);
    
    console.log('参数总和:', sum);
}

processArguments(10, 20, 30, 40, 50);
// 参数数组:[10, 20, 30, 40, 50]
// 参数总和:150

// NodeList类数组对象(如果在浏览器环境)
// let divs = document.querySelectorAll('div');
// let divArray = Array.prototype.slice.apply(divs);

场景四:数值计算的高级应用

let grades = [
    { subject: '数学', score: 85 },
    { subject: '语文', score: 92 },
    { subject: '英语', score: 78 },
    { subject: '物理', score: 88 }
];

// 提取所有分数
let scores = [];
grades.forEach(function(item) {
    scores.push.apply(scores, [item.score]);
});

console.log('所有分数:', scores);  // [85, 92, 78, 88]

// 计算平均分
let average = scores.reduce(function(sum, score) {
    return sum + score;
}, 0) / scores.length;

console.log('平均分:', average.toFixed(2));  // 85.75

// 使用apply求总分
let total = 0;
Array.prototype.forEach.apply(scores, [function(score) {
    total += score;
}]);
console.log('总分:', total);  // 343

apply()与展开运算符的对比

ES6引入的展开运算符在很多场景可以替代apply():

let numbers = [4, 2, 8, 6, 1, 9, 3, 7, 5];

// 传统apply方式
let maxOld = Math.max.apply(null, numbers);
let minOld = Math.min.apply(null, numbers);

// 现在展开运算符方式
let maxNew = Math.max(...numbers);
let minNew = Math.min(...numbers);

console.log('较大值:', maxOld, maxNew);  // 9 9
console.log('小值:', minOld, minNew);  // 1 1

// 数组合并
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

// apply方式
Array.prototype.push.apply(arr1, arr2);
console.log('apply合并:', arr1);  // [1, 2, 3, 4, 5, 6]

// 展开运算符方式
let combined = [...arr1, ...arr2];
console.log('展开合并:', combined);  // [1, 2, 3, 4, 5, 6]

个人建议:在新项目中,优先使用展开运算符,代码更简洁易读。但在以下情况,apply()仍有价值:

  • 需要兼容旧浏览器(如IE)

  • 动态决定参数(参数数组在运行时才能确定)

  • 借用某些需要以数组形式传递参数的API

高阶应用:函数柯里化与参数绑定

function multiply(a, b, c) {
    return a * b * c;
}

// 使用apply实现部分参数应用
function curry(func, ...fixedArgs) {
    return function(...remainingArgs) {
        return func.apply(null, fixedArgs.concat(remainingArgs));
    };
}

let multiplyByTwo = curry(multiply, 2);
console.log(multiplyByTwo(3, 4));  // 24(2*3*4)

let multiplyByTwoAndThree = curry(multiply, 2, 3);
console.log(multiplyByTwoAndThree(5));  // 30(2*3*5)

性能考量与实际应用

function performanceTest() {
    let arr = [1, 2, 3, 4, 5];
    
    console.time('直接调用');
    for (let i = 0; i < 1000000; i++) {
        Math.max(arr[0], arr[1], arr[2], arr[3], arr[4]);
    }
    console.timeEnd('直接调用');
    
    console.time('apply调用');
    for (let i = 0; i < 1000000; i++) {
        Math.max.apply(null, arr);
    }
    console.timeEnd('apply调用');
    
    console.time('展开运算符');
    for (let i = 0; i < 1000000; i++) {
        Math.max(...arr);
    }
    console.timeEnd('展开运算符');
}

// 性能测试(实际执行会看到差异)
// performanceTest();

课程知识要点

  1. 基本定义:apply()方法调用函数,显式指定this值,以数组形式传递参数

  2. 与call()的区别:参数传递方式不同(数组vs列表)

  3. 经典应用:查找数组较大/小值、数组合并、对象借用方法

  4. 类数组处理:将arguments、NodeList等转换为真正数组

  5. 构造函数借用:实现类似继承的功能

  6. this绑定:第一个参数控制函数执行时的this指向

  7. 现在替代:展开运算符提供了更简洁的语法

开发实践建议

基于多年JavaScript开发经验,我对apply()的使用建议如下:

推荐使用场景:

  • 处理动态生成的参数数组

  • 需要兼容旧版浏览器

  • 在函数式编程中组合参数

  • 需要借用某些需要数组形式参数的API

考虑使用现在写法的场景:

  • 简单的数组转参数可以使用展开运算符

  • 数组合并可以使用concat或展开运算符

  • 类数组转换可以使用Array.from()或展开运算符

// 现在开发实践示例
class Calculator {
    constructor(base = 0) {
        this.base = base;
    }
    
    // 使用展开运算符接收不定参数
    sum(...numbers) {
        return numbers.reduce((total, num) => total + num, this.base);
    }
    
    // 处理数组参数
    average(numbers) {
        if (!Array.isArray(numbers) || numbers.length === 0) return 0;
        return this.sum(...numbers) / numbers.length;
    }
}

let calc = new Calculator(10);
console.log(calc.sum(1, 2, 3, 4, 5));  // 25
console.log(calc.average([10, 20, 30]));  // 20

apply()方法作为JavaScript函数式编程的重要工具,理解它能够帮助我们更深入地掌握函数的调用机制和this绑定原理。虽然现在JavaScript提供了更多语法糖,但apply()在特定场景下仍然是不可或缺的工具。

← JavaScript调用函数 call() JavaScript函数 bind() →
分享笔记 (共有 篇笔记)
验证码:
微信公众号