JavaScript基础(八):函数

文章目录

JavaScript 函数是代码复用、逻辑封装的核心机制,以下从 基础定义调用方式核心特性高级用法 四个维度,系统讲解 JS 函数的核心知识点,附带实用示例:

一.函数基础(定义与调用)

函数的核心是「接收输入(参数)、执行逻辑、返回输出(返回值)」,JS 中定义函数有 4 种常用方式:

语法:

javascript 复制代码
function name(params) {
  return result;
}

1.函数声明(Function Declaration)

最传统的方式,存在变量提升(可在定义前调用).

javascript 复制代码
// 语法:function 函数名(参数 1, 参数 2) { 函数体 }
function add(a, b) {
    return a + b; // return 终止函数执行并返回结果(无 return 则返回 undefined)
}

// 调用:函数名(实参 1, 实参 2)
console.log(add(2, 3)); // 输出 5
console.log(add(1)); // 输出 NaN(b 未传值,默认为 undefined,1 + undefined = NaN)

2.函数表达式(Function Expression)

将函数赋值给变量,不存在变量提升(定义前调用会报错),可匿名或命名.

javascript 复制代码
// 匿名函数表达式
const multiply = function(a, b) {
    return a * b;
};

// 命名函数表达式(函数名仅在函数内部可用)
const divide = function calc(a, b) {
    if (b === 0) throw new Error('除数不能为 0');
    return a / b;
};

console.log(multiply(4, 5)); // 输出 20
console.log(divide(10, 2)); // 输出 5
// console.log(calc(10,2)); // 报错:calc is not defined(外部不可用)

3.箭头函数(Arrow Function,ES6+)

简洁语法,无自己的 thisargumentsprototype,不能作为构造函数.

javascript 复制代码
// 语法:(参数 1, 参数 2) => { 函数体 } (单参数可省略括号,单语句返回可省略大括号和 return)
const subtract = (a, b) => a - b; // 简化写法(等价于 { return a - b; })
const double = (num) => num * 2; // 单参数省略括号
const greet = () => console.log('Hello!'); // 无参数需写空括号

console.log(subtract(10, 3)); // 输出 7
console.log(double(6)); // 输出 12
greet(); // 输出 Hello!

4.构造函数(Function Constructor)

不推荐日常使用(可读性差、易引发安全问题),了解即可:

javascript 复制代码
const power = new Function('base', 'exponent', 'return base ** exponent');
console.log(power(2, 3)); // 输出 8

二.函数核心特性

1.参数与默认值

JS 函数参数灵活,支持 默认值剩余参数解构参数:

javascript 复制代码
// 1.默认参数(ES6+):实参未传时使用默认值
function greet(name = 'Guest') {
    return `Hello, ${name}!`;
}
console.log(greet()); // 输出 Hello, Guest!
console.log(greet('Alice')); // 输出 Hello, Alice!

// 2.剩余参数(...rest):接收多个实参并转为数组(必须在参数列表最后)
function sum(...nums) {
    return nums.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出 10

// 3.解构参数:直接解构对象/数组作为参数
function getUserInfo({ name, age }) {
    return `姓名:${name},年龄:${age}`;
}
const user = { name: 'Bob', age: 25 };
console.log(getUserInfo(user)); // 输出 姓名:Bob,年龄:25

2.this 指向(关键!)

this 是函数执行时的「上下文对象」,指向由 调用方式 决定(箭头函数无独立 this):

调用方式 this 指向 示例
普通函数调用(func ()) 全局对象(浏览器:window,Node:global) function foo(){console.log(this); } foo(); // window
对象方法调用(obj.func ()) 调用该方法的对象 const obj = { name: "A", say(){console.log(this.name); } }; obj.say(); // "A"
构造函数调用(new Func ()) 新创建的实例对象 function Person(name) { this.name = name; } const p = new Person("C"); console.log(p.name); // "C"
apply/call/bind 调用 手动指定的对象 function bar() { console.log(this.x); } bar.call({ x: 10 }); // 10

箭头函数的 this:

继承自外层「非箭头函数」的 this,且永久绑定(无法通过 apply/call/bind 修改):

javascript 复制代码
const obj = {
    name: 'D',
    normalFunc: function() {
        const arrowFunc = () => console.log(this.name); // this 继承自 normalFunc 的 this(即 obj)
        arrowFunc();
    }
};
obj.normalFunc(); // 输出 "D"

3.闭包(Closure)

核心定义:函数嵌套时,内层函数引用外层函数的变量 / 参数,且内层函数被外部访问,导致外层函数的作用域不会被销毁.用途:封装私有变量、实现数据缓存、模块化.

示例: 实现计数器(私有变量 count 无法被外部直接修改):

javascript 复制代码
function createCounter() {
    let count = 0; // 外层函数变量,被内层函数引用
    return function() {
        count++; // 内层函数访问外层变量
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(count); // 报错:count is not defined(外部无法访问)

4.立即执行函数(IIFE)

定义后立即执行,用于隔离作用域(避免变量污染全局),ES6 后可被 let/const 块级作用域替代,但仍有兼容场景使用:

javascript 复制代码
// 语法 1:(函数)()
(function(a, b) {
    console.log(a + b); // 输出 5
})(2, 3);

// 语法 2:函数()()
(function(a, b) {
    console.log(a * b); // 输出 12
})(4, 3);

三.高级用法

1.高阶函数(Higher-Order Function)

接收函数作为参数,或返回函数的函数(JS 数组方法如 mapfilterreduce 都是高阶函数):

javascript 复制代码
// 示例 1:接收函数作为参数
function processArray(arr, handler) {
    const result = [];
    for (const item of arr) {
        result.push(handler(item)); // 执行传入的处理函数
    }
    return result;
}

const numbers = [1, 2, 3];
const doubled = processArray(numbers, (num) => num * 2); // 传入箭头函数
console.log(doubled); // [2,4,6]

// 示例 2:返回函数
function makeMultiplier(factor) {
    return function(num) {
        // 返回函数
        return num * factor;
    };
}

const multiplyBy3 = makeMultiplier(3);
console.log(multiplyBy3(5)); // 15

2.函数柯里化(Currying)

将多参数函数转为「单参数函数链」,逐步接收参数并返回新函数,直到参数齐全后执行:

javascript 复制代码
// 柯里化函数:sum(a, b, c) → sum(a)(b)(c)
function currySum(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

console.log(currySum(1)(2)(3)); // 6

// 箭头函数简化柯里化
const curryMultiply = (a) => (b) => (c) => a * b * c;
console.log(curryMultiply(2)(3)(4)); // 24

3.函数防抖与节流(性能优化)

  • 防抖(Debounce): 触发后延迟 n 秒执行,若 n 秒内再次触发则重新计时(适用于输入框搜索、窗口 resize).
  • 节流(Throttle): 触发后立即执行,然后 n 秒内不响应后续触发(适用于滚动事件、按钮点击防重复提交).

示例实现:

javascript 复制代码
// 防抖
function debounce(fn, delay = 500) {
    let timer = null;
    return function(...args) {
        clearTimeout(timer); // 清除之前的定时器
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

// 节流
function throttle(fn, interval = 500) {
    let lastTime = 0;
    return function(...args) {
        const now = Date.now();
        if (now - lastTime >= interval) {
            // 间隔满足则执行
            fn.apply(this, args);
            lastTime = now;
        }
    };
}

// 用法:输入框搜索防抖
const searchInput = document.getElementById('search');
searchInput.addEventListener(
    'input',
    debounce(function(e) {
        console.log('搜索:', e.target.value);
    }, 300)
);

四.常见注意点

  1. 参数个数不匹配:实参多于形参时,多余参数可通过 arguments(类数组)或剩余参数获取;实参少于形参时,未传参数默认为 undefined.
  2. 箭头函数的限制:不能作为构造函数(new 会报错)、无 prototype、无 arguments 对象(需用剩余参数替代).
  3. 闭包内存泄漏:若闭包引用的变量过大且长期不释放,可能导致内存泄漏,需及时解除引用(如 counter = null).
  4. this 丢失问题:对象方法赋值给变量后调用,this 会指向全局(可通过 bind 或箭头函数解决):
javascript 复制代码
const obj = {
    name: 'E',
    say() {
        console.log(this.name);
    }
};
const say = obj.say;
say(); // 输出 undefined(this 指向 window)
const boundSay = obj.say.bind(obj);
boundSay(); // 输出 "E"(bind 绑定 this)
相关推荐
我是阿亮啊1 小时前
搭建Vue环境遇到的问题
javascript·vue.js·npm·node.js
〝七夜5691 小时前
JVM内存结构
java·开发语言·jvm
初级炼丹师(爱说实话版)1 小时前
JAVA泛型作用域与静态方法泛型使用笔记
java·开发语言·笔记
技术净胜2 小时前
MATLAB二维绘图教程:plot()函数全解析(线条样式/颜色/标记/坐标轴设置)
开发语言·matlab
Slow菜鸟2 小时前
Java开发规范(八)| 安全规范—企业级应用的“架构级底线”
java·开发语言·安全
憨憨崽&2 小时前
进击大厂:程序员必须修炼的算法“内功”与思维体系
开发语言·数据结构·算法·链表·贪心算法·线性回归·动态规划
han_2 小时前
Vue.js 为什么要推出 Vapor Mode?
前端·javascript·vue.js
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于Java的公职备考在线学习系统的设计与实现为例,包含答辩的问题和答案
java·开发语言·学习
二川bro3 小时前
模型部署实战:Python结合ONNX与TensorRT
开发语言·python