← JavaScript匿名函数 JavaScript函数 apply() →

JavaScript调用函数 call()

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

JavaScript call()方法详解:灵活控制函数调用方式

什么是call()方法?

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

call()方法让我们能够控制函数执行时的上下文(this的值),这在很多场景下非常有用。

// 基本语法
functionName.call(thisArg, arg1, arg2, ...)

参数说明:

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

  • arg1, arg2, ...:可选参数,传递给函数的参数列表

call()方法的核心用途

1. 借用构造函数实现继承

这是call()方法最经典的应用场景之一:

// 基类构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.type = '人类';
}

// 学生构造函数
function Student(name, age, studentId) {
    // 借用Person构造函数
    Person.call(this, name, age);
    this.studentId = studentId;
    this.role = '学生';
}

// 教师构造函数
function Teacher(name, age, teacherId) {
    // 借用Person构造函数
    Person.call(this, name, age);
    this.teacherId = teacherId;
    this.role = '教师';
}

let student = new Student('张三', 18, '2024001');
let teacher = new Teacher('李四', 35, 'T2024');

console.log(student);  // {name: '张三', age: 18, type: '人类', studentId: '2024001', role: '学生'}
console.log(teacher);  // {name: '李四', age: 35, type: '人类', teacherId: 'T2024', role: '教师'}

个人见解:虽然ES6引入了class语法和extends关键字,但在某些需要动态继承的场景,或者处理旧代码库时,call()方法仍然是有效的工具。理解call()有助于深入理解JavaScript的原型链机制。

2. 借用他对象的方法

let calculator = {
    base: 10,
    add: function(a, b) {
        return this.base + a + b;
    },
    multiply: function(a, b) {
        return this.base * a * b;
    }
};

let anotherCalc = {
    base: 5
};

// 借用add方法
console.log(calculator.add(3, 4));  // 17(10+3+4)
console.log(calculator.add.call(anotherCalc, 3, 4));  // 12(5+3+4)

// 借用multiply方法
console.log(calculator.multiply(2, 3));  // 60(10*2*3)
console.log(calculator.multiply.call(anotherCalc, 2, 3));  // 30(5*2*3)

3. 将类数组对象转换为数组

在ES6之前,经常使用call()来将arguments转换为真正的数组:

function sum() {
    // arguments是类数组对象,不是真正的数组
    console.log(Array.isArray(arguments));  // false
    
    // 使用call()借用数组的slice方法
    let args = Array.prototype.slice.call(arguments);
    console.log(Array.isArray(args));  // true
    
    return args.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4, 5));  // 15

现在替代方案:现在可以使用Array.from()或展开运算符更方便地实现:

function sum() {
    let args = Array.from(arguments);
    // 或
    // let args = [...arguments];
    return args.reduce((total, num) => total + num, 0);
}

call()与apply()的区别

这两个方能相似,主要区别在于参数传递方式:

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

let person = { name: '王五' };

// call()接受参数列表
greet.call(person, '你好', '!');  // 你好,王五!

// apply()接受参数数组
greet.apply(person, ['你好', '!']);  // 你好,王五!

实际应用场景

场景一:调用父类构造函数

function Product(name, price) {
    this.name = name;
    this.price = price;
}

function Food(name, price, category) {
    Product.call(this, name, price);
    this.category = category;
}

function Toy(name, price, ageGroup) {
    Product.call(this, name, price);
    this.ageGroup = ageGroup;
}

let apple = new Food('苹果', 5, '水果');
let car = new Toy('玩具车', 89, '3-6岁');

console.log(apple);  // {name: '苹果', price: 5, category: '水果'}
console.log(car);    // {name: '玩具车', price: 89, ageGroup: '3-6岁'}

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

function Shape(type) {
    this.type = type;
    this.created = new Date();
}

function Circle(radius) {
    Shape.call(this, '圆形');
    this.radius = radius;
    this.area = function() {
        return Math.PI * this.radius * this.radius;
    };
}

function Rectangle(width, height) {
    Shape.call(this, '矩形');
    this.width = width;
    this.height = height;
    this.area = function() {
        return this.width * this.height;
    };
}

let circle = new Circle(5);
let rectangle = new Rectangle(4, 6);

console.log(circle.type, circle.area());     // 圆形 78.53981633974483
console.log(rectangle.type, rectangle.area()); // 矩形 24

场景三:调用原生方法

let str = 'Hello World';

// 借用字符串方法
String.prototype.reverse = function() {
    return this.split('').reverse().join('');
};

console.log(str.reverse());  // dlroW olleH

// 借用数组方法处理类数组对象
function printArguments() {
    // 借用数组的forEach方法
    Array.prototype.forEach.call(arguments, function(arg, index) {
        console.log(`参数${index}:`, arg);
    });
}

printArguments('a', 'b', 'c', 'd');

call()与this绑定的深入理解

let obj = {
    name: 'obj',
    showName: function() {
        console.log(this.name);
    }
};

let obj2 = {
    name: 'obj2'
};

obj.showName();              // obj
obj.showName.call(obj2);     // obj2
obj.showName.call(null);     // undefined(严格模式下this指向null,非严格模式指向全局对象)

关于this指向的注意事项:

'use strict';

function strictModeTest() {
    console.log(this);
}

strictModeTest.call(undefined);  // undefined
strictModeTest.call(null);       // null

性能考量

在性能敏感的场景,直接调用函数比使用call()更快:

function add(a, b) {
    return a + b;
}

// 直接调用
console.time('直接调用');
for (let i = 0; i < 1000000; i++) {
    add(1, 2);
}
console.timeEnd('直接调用');

// 使用call()
console.time('call调用');
for (let i = 0; i < 1000000; i++) {
    add.call(null, 1, 2);
}
console.timeEnd('call调用');

个人建议:在大多数日常开发中,性能差异可以忽略不计,优先考虑代码的可读性和维护性。只有在极高性能要求的场景(如游戏引擎、动画循环)才需要关注这种微优化。

课程知识要点

  1. 基本功能:call()方法用于调用函数,并显式指定this的值

  2. 参数传递:接受参数列表,而不是数组

  3. 构造函数借用:实现继承的经典方式

  4. 方法借用:一个对象可以借用另一个对象的方法

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

  6. this绑定规则:call()的第一个参数决定this指向

  7. 严格模式影响:传入null/undefined时,严格模式下this为null/undefined,非严格模式指向全局对象

现在开发建议

基于多年的JavaScript开发经验,我对call()方法的使用有一些体会:

适用场景:

  • 需要显式指定this指向时

  • 实现多重继承或混入模式

  • 借用他对象的方法

  • 处理遗留代码或需要兼容旧环境

可考虑替代方案的场景:

  • 简单的this绑定可以使用箭头函数(词法作用域)

  • 需要固定this时可以bind()

  • 现在继承可以使用class语法

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

// 现在写法替代call()的场景

// 1. 使用展开运算符替代数组转换
function sum(...args) {
    return args.reduce((a, b) => a + b);
}

// 2. 使用class语法替代构造函数借用
class Product {
    constructor(name, price) {
        this.name = name;
        this.price = price;
    }
}

class Food extends Product {
    constructor(name, price, category) {
        super(name, price);
        this.category = category;
    }
}

// 3. 使用箭头函数绑定this
class Timer {
    constructor() {
        this.seconds = 0;
        // 箭头函数自动绑定外层this
        setInterval(() => {
            this.seconds++;
        }, 1000);
    }
}

call()方法是JavaScript函数式特性的重要组成部分,理解它能够帮助你更深入地掌握JavaScript的this机制和函数调用原理。虽然在很多场景下有了更现在的替代方案,但在处理特定问题或维护旧代码时,call()仍然是不可或缺的工具。

← JavaScript匿名函数 JavaScript函数 apply() →
分享笔记 (共有 篇笔记)
验证码:
微信公众号