函数调用
函数调用的本质
在 JavaScript 中,函数调用的本质是:通过调用运算符 () 触发 JS 引擎为目标函数创建并执行一个独立的执行上下文(Execution Context),按序执行函数体代码,最终返回执行结果(若无显式返回则返回 undefined)的完整过程。
这个定义包含 4 个不可拆分的核心要素,缺一不可:
- 触发条件:必须通过
()调用运算符触发(这是调用的显性标识); - 核心动作:JS 引擎为函数创建专属的执行上下文(这是底层核心);
- 执行过程:按顺序执行函数体中的代码(这是调用的目的);
- 结果输出:执行完毕后返回结果(这是调用的最终产出)。
常见的调用模式
| 调用模式 | this指向 |
|---|---|
| 方法调用模式 | 调用该方法的对象 |
| 函数调用模式 | 非严格模式->全局对象;严格时->undefined |
| 构造器调用模式 | 新创建的实例对象 |
| Apply调用模式 | 手动指定的第一个参数 |
调用模式的具体使用
方法调用模式
一、方法调用模式核心定义
方法调用模式指:将函数作为对象的属性(方法),通过 对象.方法名() 的形式调用。核心特征是函数内部的 this 指向调用该方法的对象,这是它和普通调用模式最本质的区别。
二、代码示例展示
// 1. 定义一个包含属性和方法的对象
const person = {
// 数据属性(存储数据)
name: "小明",
// 方法(函数作为对象属性)
sayHello: function() {
// this 指向调用该方法的 person 对象
console.log(`你好,我是${this.name}`);
}
};
// 2. 方法调用模式:对象.方法名()
person.sayHello(); // 输出:你好,我是小明
函数调用模式
一、普通函数调用模式的准确定义
函数调用模式(也叫独立函数调用模式)是指:直接通过 函数名()(或匿名函数后加 ())的形式触发函数执行,函数既不作为对象的方法、也不通过 new/call/apply/bind 等特殊方式调用。该模式的核心特征是:函数内部的 this 指向有明确规则 —— 非严格模式下指向全局对象(浏览器为 window,Node.js 为 global),严格模式下为 undefined。
二、代码展示
// 1. 定义普通函数
function sayHi() {
// 非严格模式下 this 指向 window
console.log("Hello!", "this指向:", this);
}
// 2. 普通函数调用:直接函数名+()
sayHi();
// 输出:Hello! this指向:Window(浏览器环境)
构造器调用模式
一、构造器调用模式的准确定义
构造器调用模式(也叫 new 调用模式)是指:通过 new 关键字后跟函数名(new 函数名())的形式调用函数,此时该函数会作为 “构造函数” 使用,核心目的是创建并返回一个新的对象实例。该模式的核心特征:
- 函数内部的 this 会自动绑定到新创建的空对象上;
- 无需手动写 return,JS 引擎会自动返回这个新对象(除非手动返回一个非原始值的对象);
- 约定俗成:构造函数的函数名首字母大写(如 Person、Car),区分普通函数。
二、代码展示
// 1. 定义构造函数(首字母大写,约定俗成)
function Person(name, age) {
// this 指向新创建的空对象
this.name = name; // 给新对象添加属性
this.age = age;
this.sayHi = function() { // 给新对象添加方法
console.log(`我是${this.name},今年${this.age}岁`);
};
}
// 2. 构造器调用模式:new + 构造函数名()
const person1 = new Person('小明', 18);
const person2 = new Person('小红', 20);
// 调用实例的方法(this 指向各自的实例)
person1.sayHi(); // 输出:我是小明,今年18岁
person2.sayHi(); // 输出:我是小红,今年20岁
Apply调用模式
一、apply 调用模式的准确定义
apply 调用模式是指:通过函数的内置 apply() 方法触发函数执行,可手动指定函数内部 this 的指向,且函数参数以数组(或类数组)的形式传递。该模式的核心特征:
- apply() 的第一个参数是要绑定给 this 的对象(若传 null/undefined,非严格模式下 this 指向全局对象);
- 第二个参数必须是数组 / 类数组(如 arguments、DOM 节点列表),数组中的元素会依次作为函数的参数;
- 调用后立即执行函数,这是它和 bind 最核心的区别(bind 仅返回新函数,不立即执行)。
二、代码展示
// 1. 定义普通函数
function showInfo(job, hobby) {
// this 指向 apply 第一个参数指定的对象
console.log(`姓名:${this.name},职业:${job},爱好:${hobby}`);
}
// 2. 准备要绑定给 this 的对象 const person = { name: '小明' };
// 3. apply 调用模式:手动指定 this + 数组传参
showInfo.apply(person, ['程序员', '打球']);
// 输出:姓名:小明,职业:程序员,爱好:打球
总结
- 这常见的四种模式的核心差异在于 this 指向和调用语法,其中 this 指向是区分的关键;
- 方法调用是面向对象封装的核心,构造器调用是创建实例的核心,apply 调用是灵活控制 this 的核心,普通调用是最基础但易踩坑的模式;
- 实际开发中,优先使用方法调用 / 构造器调用(封装性好),普通调用需注意 this 污染,apply 调用适配数组参数场景。 记住 “调用方式定 this,场景需求选模式”,就能快速判断并选择合适的函数调用方式。