JavaScript 函数是 JS 的核心灵魂,理解其底层机制不仅能提升代码质量,更能应对复杂场景(列如框架源码、设计模式)。以下从核心机制入手,结合实例拆解,帮你彻底吃透函数本质。
一、函数的「定义本质」:不是 “代码块”,而是 “可执行对象”
JS 函数的本质是Function 类型的实例(和对象、数组一样是 “值”),这意味着:
- 可以被赋值给变量 / 属性
- 可以作为参数传递
- 可以作为返回值返回
- 有自己的属性和方法(如name、length、call())
常见定义方式及核心差异
1.函数声明(Function Declaration)
function foo() {} // 有函数提升(hoisting),在作用域内任何位置可调用
特点:会被整体提升到作用域顶部(比变量提升优先级高),可在定义前调用。
2.函数表达式(Function Expression)
const foo = function() {}; // 无函数提升,仅变量提升(值为undefined)
特点:本质是 “变量赋值”,定义前调用会报错(变量已声明但值为 undefined)。
3.箭头函数(Arrow Function)
const foo = () => {}; // 无this绑定、无arguments、不能作构造函数
本质是简化版函数表达式,但有 3 个关键限制(后面详解)。
4.Function 构造函数(不推荐)
const foo = new Function('a', 'b', 'return a + b'); // 字符串解析,性能差且不安全
特点:作用域是全局(而非当前作用域),几乎不用(除非动态生成代码)。

二、函数的「调用与 this 绑定」:this 指向由 “调用方式” 决定
this 是函数执行时的 “上下文对象”,其指向完全由调用方式决定,和定义位置无关。
4种核心调用方式及 this 指向
1.普通调用(独立调用)
function foo() { console.log(this); }
foo(); // 非严格模式:this → window;严格模式:this → undefined
本质:函数作为 “独立函数” 执行,无明确归属对象。
2.对象方法调用
const obj = {
name: 'obj',
foo() { console.log(this.name); }
};
obj.foo(); // this → obj(调用时的“点前面”的对象)
关键:this 指向调用时的直接所属对象(而非定义时的对象):
const bar = obj.foo;
bar(); // 此时是普通调用,this → window/undefined(和obj无关了)
3.构造函数调用(new)
function Person(name) {
this.name = name; // this → 新创建的实例对象
}
const p = new Person('张三');
new 操作会强制 this 指向新生成的实例,且默认返回该实例(若手动 return 对象则覆盖)。
4.强制改变 this(call/apply/bind)
- call(obj, arg1, arg2):立即执行,参数逐个传
- apply(obj, [arg1, arg2]):立即执行,参数用数组传
- bind(obj, arg1, arg2):返回新函数(不执行),参数预设
function foo(a, b) { console.log(this.x + a + b); }
const obj = { x: 10 };
foo.call(obj, 2, 3); // 10+2+3=15(this→obj)
foo.apply(obj, [2, 3]); // 同上
const bar = foo.bind(obj, 2);
bar(3); // 10+2+3=15(bind预设了this和部分参数)
5.箭头函数的 this:“继承” 外层作用域的 this
箭头函数没有自己的 this,其 this 永远等于定义时外层作用域的 this(且无法通过 call/apply/bind 改变):
const obj = {
foo() {
const bar = () => { console.log(this); }; // this继承foo的this(即obj)
bar();
}
};
obj.foo(); // 输出obj
场景:解决回调函数中 this 丢失问题(如定时器、事件处理)。

三、函数的「作用域与闭包」:变量的 “生存规则”
作用域是函数创建时生成的 “变量访问边界”,闭包是作用域的 “特殊产物”。
1.作用域核心规则
- 函数作用域:每个函数创建一个独立作用域,内部变量外部不可见
- 作用域链:内部作用域可访问外部作用域变量(由内向外查找,找到即止)
const x = 10;
function outer() {
const y = 20;
function inner() {
const z = 30;
console.log(x + y + z); // 10+20+30=60(访问外层变量)
}
inner();
}
outer();
2.闭包:函数 “记住” 外层变量
当内部函数被外部引用时,即使外部函数执行完毕,其作用域也不会销毁(内部函数仍在使用),这种现象就是闭包。
function outer() {
let count = 0;
return function inner() { // inner被外部引用,形成闭包
count++;
return count;
};
}
const fn = outer();
console.log(fn()); // 1(count未被销毁)
console.log(fn()); // 2(继续累加)
核心价值:
- 实现私有变量(外部无法直接修改 count)
- 模块化(隔离作用域,避免全局污染)
- 延迟执行(如定时器、防抖节流)
注意:过度使用闭包可能导致内存泄漏(作用域不销毁),需及时解除引用(fn = null)。

四、函数的「参数机制」:灵活但需注意细节
JS 函数参数无类型限制,数量可动态调整,核心依赖arguments和 “参数默认值”。
1. arguments 对象(箭头函数无)
函数内部的arguments是类数组对象,包含所有实参(与形参 “松散绑定”):
function foo(a, b) {
console.log(arguments[0]); // 1(实参)
console.log(arguments.length); // 2
a = 100;
console.log(arguments[0]); // 非严格模式:100(形参与arguments绑定);严格模式:1(不绑定)
}
foo(1, 2);
现代 JS 更推荐用剩余参数(…rest)(真正的数组,且更灵活):
function foo(...rest) { // rest是数组:[1,2,3]
console.log(rest.reduce((a,b) => a+b)); // 6
}
foo(1,2,3);
2. 参数默认值
形参可设置默认值,当实参未传或为undefined时生效:
function foo(a = 10, b = 20) {
return a + b;
}
foo(); // 30(a=10, b=20)
foo(5); // 25(a=5, b=20)
注意:默认值是 “运行时计算” 的,每次调用都会重新计算:
let x = 10;
function foo(a = x++) { return a; }
foo(); // 10(x变为11)
foo(); // 11(x变为12)

五、函数的「属性与方法」:隐藏的实用工具
函数作为对象,有自带属性和方法:
1.name:函数名(箭头函数的 name 由赋值变量决定)
function foo() {};
const bar = () => {};
console.log(foo.name); // "foo"
console.log(bar.name); // "bar"
2.length:形参数量(不包含默认值参数和剩余参数)
function foo(a, b = 10, ...rest) {}.
console.log(foo.length); // 1(仅a是无默认值的形参)
总结:核心机制串联
函数的本质是 “可执行对象”,其行为由:
- 定义方式决定提升规则和特性(如箭头函数无 this)
- 调用方式决定 this 指向
- 作用域链决定变量访问范围,进而产生闭包
- 参数机制决定实参形参的关联方式

深入学习能提升代码质量
就这?这不是基础吗?