← [ BACK_TO_NOTES ]

JavaScript 函数总览

JS 函数的三层身份(可调用代码块、对象、作用域边界)、定义方式、参数系统、this、闭包、高阶函数、特殊函数。

#synthesis #evergreen #javascript #ecmascript

[!info] related notes

JavaScript 函数总览

函数的三层身份

JS 函数不是”特殊语法糖”,它同时是:

  1. 可执行的代码块 — 能被调用
  2. 一种对象 — 能赋值、传参、返回、挂属性
  3. 一种作用域边界 — 形成局部变量、闭包、词法环境
function fn(a, b) { return a + b }

typeof fn      // "function"
fn(1, 2)       // 3
fn.x = 100     // 可以挂属性
fn.x           // 100

一、定义方式

函数声明

function add(x, y) { return x + y }
  • 有函数名
  • 会提升(hoist):声明前可调用
  • typeof add === "function"

函数表达式

const add = function(x, y) { return x + y }
  • 变量受 var / let / const 规则控制
  • 函数本身不整体提升到可调用状态
  • 可匿名,也可具名

匿名函数表达式

const f = function() {}

具名函数表达式

const c = function d() {
  console.log(typeof d)  // "function"(内部可见)
}
console.log(typeof c)    // "function"
console.log(typeof d)    // "undefined"(外部不可见)

用途:

  • 递归更稳定(内部名不依赖外部变量)
  • 调试堆栈信息更清楚

箭头函数

const add = (a, b) => a + b
const square = x => x * x
const getObj = () => ({ x: 1 })

详见下方「箭头函数」章节。

构造器方式

const add = new Function('x', 'y', 'return x + y')
  • 运行时动态拼代码
  • 性能和安全性差,默认全局作用域
  • 工程中极少使用

二、提升(Hoisting)

定义方式提升行为
函数声明整体提升,声明前可调用
var 函数表达式只提升变量声明(var foo),值为 undefined
let / const 函数表达式暂时性死区(TDZ),声明前访问报 ReferenceError
箭头函数(const)同 let / const,有 TDZ
foo()          // ok
function foo() {}

// bar()       // TypeError: bar is not a function
var bar = function() {}

// baz()       // ReferenceError: Cannot access 'baz' before initialization
const baz = function() {}

三、参数系统

详见 arguments-object。

命名参数

function test(a, b) { console.log(a, b) }
test(1)        // 1, undefined(少传)
test(1, 2, 3)  // 1, 2(多传忽略)

JS 不强制参数个数严格一致。

默认参数

function greet(name = 'world') { return 'hello ' + name }

greet()          // "hello world"
greet(undefined) // "hello world"(undefined 触发默认值)
greet(null)      // "hello null"(null 不触发)

只有 undefined 触发默认值,不是所有假值。

剩余参数 rest

function sum(...nums) { return nums.reduce((a, b) => a + b, 0) }

...nums 收集剩余实参成为真正的数组。

arguments

普通函数内自动可用的类数组对象。箭头函数没有 arguments。

function log(a, b, c, d) {
  console.log(a, b, c, d);  // 1, 2, 3, undefined
  arguments[0] = 'bfe';
  arguments[3] = 'dev';
  console.log(a, b, c, d);  // "bfe", 2, 3, undefined
}
log(1, 2, 3);

关键行为:非严格模式下 arguments[n] 和命名参数双向同步——但仅限实际传递的参数。超出范围的赋值只作用于 arguments 对象本身。

四、this

详见 this 关键字。

普通函数的 this 不是定义时决定的,而是调用时决定的。

调用方式与 this

调用方式this 指向示例
普通调用 fn()非严格:window / global;严格:undefinedfn()
方法调用 obj.fn()调用它的对象obj.fn()
构造调用 new Fn()新创建的对象new Person()
显式绑定指定的 thisArgfn.call(obj), fn.apply(obj)

call / apply / bind

详见 call-apply-bind。

function foo(a, b) { console.log(this.x, a, b) }
const obj = { x: 100 }

foo.call(obj, 1, 2)     // 立即调用,参数逐个传
foo.apply(obj, [1, 2])  // 立即调用,参数数组传
const bar = foo.bind(obj, 1)  // 不立即调用,返回新函数
bar(2)

容易踩坑:方法被拿出来调用

const obj = {
  x: 10,
  foo() { console.log(this.x) }
}
const f = obj.foo
f() // undefined(已经不是 obj.foo(),是普通函数调用)

五、箭头函数

与普通函数的区别

特性普通函数箭头函数
this调用时决定继承外层作用域,不可被 call/apply/bind 改变
arguments有自己的没有,继承外层
new 构造可以不可以(TypeError)
prototype有undefined
写法function() {}() => {}

箭头函数 this 的经典用法

对象方法不要用箭头函数(this 不指向对象):

const obj = {
  x: 10,
  foo: () => { console.log(this.x) }  // 不会输出 10
}

回调函数常用箭头函数保留外层 this:

function Timer() {
  this.count = 0
  setInterval(() => {
    this.count++  // 继承 Timer 的 this
    console.log(this.count)
  }, 1000)
}

六、构造函数与 new

详见 ECMAScript 原型。

new 做了什么

const p = new Person('Tom')

等价于:

const obj = {}
obj.__proto__ = Person.prototype
Person.call(obj, 'Tom')
return obj  // 如果 Person 没返回对象的话

构造函数返回值规则

function A() { this.x = 1; return 100 }    // 返回基本类型 → 忽略,返回新对象
function B() { this.x = 1; return { y: 2 } } // 返回对象 → 替代默认新对象

new A().x  // 1
new B().x  // undefined
new B().y  // 2

七、闭包

详见 ecmascript-closures。

函数能够记住并访问它定义时所在词法作用域中的变量,即使这个函数在外部执行。

function outer() {
  let count = 0
  return function() {
    count++
    console.log(count)
  }
}
const fn = outer()
fn() // 1
fn() // 2

私有变量

function createCounter() {
  let count = 0
  return {
    inc() { count++ },
    get() { return count }
  }
}

经典坑:循环闭包

for (var i = 0; i < 3; i++) {
  setTimeout(function() { console.log(i) }, 0)
}
// 输出 3, 3, 3(共享同一个 i)

for (let i = 0; i < 3; i++) {
  setTimeout(function() { console.log(i) }, 0)
}
// 输出 0, 1, 2(let 每轮创建新绑定)

八、作用域

JS 采用词法作用域:变量查找由函数定义的位置决定,不是调用的位置。

let x = 1
function outer() {
  let x = 2
  function inner() { console.log(x) }
  return inner
}
const f = outer()
f() // 2(inner 定义时在 outer 内部,查找 outer 的 x)

九、高阶函数

接收函数作为参数,或返回函数。

// 接收函数
function map(arr, fn) {
  const res = []
  for (const item of arr) res.push(fn(item))
  return res
}

// 返回函数
function multiply(x) {
  return function(y) { return x * y }
}
const double = multiply(2)
double(5) // 10

常见高阶函数:map、filter、reduce、sort、forEach。 详见 array-prototype-map、higher-order-function-callback-mismatch。

十、柯里化

详见 currying。

把多参数函数变成一连串单参数函数,依赖闭包:

function add(a) {
  return function(b) {
    return function(c) {
      return a + b + c
    }
  }
}
add(1)(2)(3) // 6

十一、IIFE(立即执行函数)

(function() {
  console.log('run')
})()

用途:创建独立作用域、避免污染全局变量、旧时代模拟模块。

十二、递归

function factorial(n) {
  if (n <= 1) return 1
  return n * factorial(n - 1)
}

用具名函数表达式更稳定:

const factorial = function fact(n) {
  if (n <= 1) return 1
  return n * fact(n - 1)  // 不依赖外部变量名
}

十三、特殊函数类型

getter / setter

const obj = {
  _x: 1,
  get x() { return this._x },
  set x(v) { this._x = v }
}

本质也是函数,通过属性访问语法触发。

generator

function* gen() {
  yield 1
  yield 2
}
const it = gen()
it.next() // { value: 1, done: false }

调用后不直接执行,返回迭代器。详见 ecmascript-iterators-and-generators。

async

async function foo() {
  const x = await Promise.resolve(10)
  return x
}
foo().then(console.log) // 10

返回 Promise,await 等待 Promise 解析。详见 async / await。

严格模式下的函数行为

'use strict'
function foo() { console.log(this) }
foo() // undefined(非严格模式是 window)

十四、函数对象的成员

属性含义
name函数名(调试用,具名表达式返回内部名)
length形参数量(不含 rest)
prototype构造函数实例共享方法的落点,箭头函数无
caller历史属性,现代代码尽量不用

详见 function-length。

十五、最小必会清单

题型核心考点
函数声明 vs 表达式提升差异
具名函数表达式内部名可见性
闭包词法作用域捕获
循环闭包var 共享 vs let 隔离
this调用时决定
箭头函数 this继承外层,不可绑定
new 做了什么创建对象 + 原型绑定 + this 绑定 + 返回
call / apply / bind立即调用 vs 返回新函数
高阶函数回调参数参数错位陷阱

学习顺序建议

  1. 函数声明 / 表达式 / 箭头函数
  2. 作用域 / 词法环境
  3. 闭包
  4. this
  5. call / apply / bind
  6. new / prototype / 原型链
  7. Promise / async(本质也离不开函数)

学习检查

不看正文回答:

  1. JavaScript 函数为什么同时可以被理解成“可调用代码、对象、作用域边界”?
  2. 函数声明、函数表达式和箭头函数在提升与调用语义上有哪些关键差异?
  3. 普通函数的 this 为什么由调用方式决定,而箭头函数不是?
  4. 闭包为什么依赖词法作用域,而不是调用位置?
  5. call / apply / bind 与普通方法调用分别怎样影响 this?
  6. 构造调用 new Fn() 至少会发生哪些关键步骤?
  7. 高阶函数、柯里化、IIFE、generator、async function 分别是在函数这一基础对象上增加了什么使用模式?
  8. 给一段陌生函数代码,能否预测它的作用域、this、闭包和返回行为,而不是只认 API?

掌握验收

  • 能解释函数声明 / 表达式 / 箭头函数的语义边界;
  • 能独立分析 this、闭包、new 与高阶函数代码;
  • 能把具体函数问题路由到对应原子笔记,而不是在总览里继续堆第二份说明。
创建于 2025/1/1 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本