函数表达式:JavaScript中那些你不知道的优雅写法 - 教程

news/2025/11/15 16:59:20/文章来源:https://www.cnblogs.com/yangykaifa/p/19225803

在 JavaScript 的世界里,函数是一等公民,这意味着函数可以像变量一样被传递、赋值、作为参数传递给其他函数,甚至可以作为函数的返回值。而函数表达式正是这种灵活性的体现之一。今天,我们就来深入探讨一下《JavaScript 高级程序设计》第七章中关于函数表达式的那些优雅写法。

前言

函数表达式是 JavaScript 中定义函数的两种主要方式之一(另一种是函数声明)。与函数声明不同,函数表达式是在代码执行阶段创建的,而不是在解析阶段。这种特性使得函数表达式在某些场景下具有独特的优势。

在本文中,我们将通过丰富的示例,详细介绍函数表达式的定义方式、递归、闭包、模仿块级作用域以及私有变量等概念。希望通过本文的阐述,能让大家对函数表达式有更深入的理解和应用。

什么是函数表达式

函数表达式是 JavaScript 中创建函数的另一种方式。与函数声明不同,函数表达式是在代码执行阶段创建的。函数表达式的语法如下:

var functionName = function(arg0, arg1, arg2) {
// 函数体
};

这种语法创建了一个匿名函数(也称为拉姆达函数),并将其赋值给变量 functionName。由于函数表达式创建的是匿名函数,因此 function 关键字后面没有函数名。如果在 function 关键字后面加上函数名,这个函数名只在函数内部有效,在函数外部无效。

var functionName = function fnName() {
// 函数体
};
// 这里调用 fnName() 会报错

函数表达式 vs 函数声明

在 JavaScript 中,函数可以通过函数声明或函数表达式来定义。虽然它们看起来相似,但在行为上有一些重要的区别。

函数声明

函数声明会在代码执行前被解析,这意味着你可以在函数声明之前调用它。这种行为被称为函数声明提升(function declaration hoisting)。

sayHi(); // 正常执行
function sayHi() {
console.log("Hi!");
}

函数表达式

函数表达式是在代码执行阶段创建的,因此在函数表达式赋值之前调用它会导致错误。

sayHi(); // 错误!
var sayHi = function() {
console.log("Hi!");
};

在上面的例子中,由于函数提升,变量 sayHi 会被提升到作用域顶部,但其值 undefined 不会被提升,因此在赋值之前调用 sayHi 会导致 TypeError。

递归

递归是一种常见的编程技术,它指的是函数调用自身。在函数表达式中实现递归需要特别注意,因为函数表达式创建的是匿名函数。

递归示例

function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}

上面的代码定义了一个计算阶乘的递归函数。但是,如果我们将这个函数赋值给另一个变量,然后将原变量设置为 null,就会出现问题。

var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(4)); // 出错!

当调用 anotherFactorial(4) 时,由于 factorial 已经被设置为 null,因此在函数内部调用 factorial 会导致错误。

使用 arguments.callee

为了解决这个问题,可以使用 arguments.callee 来引用当前正在执行的函数。

function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}

通过使用 arguments.callee,无论函数如何被调用,都可以正确地实现递归。

严格模式下的替代方案

在严格模式下,arguments.callee 是不可用的。因此,我们需要使用命名函数表达式来实现递归。

var factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
});

通过命名函数表达式,我们可以确保函数内部可以正确地调用自身,而不会受到外部变量变化的影响。

闭包

闭包是指有权访问另一个函数作用域中变量的函数。在 JavaScript 中,闭包是函数表达式的一个重要特性。

闭包示例

function createComparisonFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}

在上面的代码中,createComparisonFunction 函数返回了一个匿名函数。这个匿名函数可以访问 createComparisonFunction 函数的参数 propertyName,即使 createComparisonFunction 函数已经执行完毕。

闭包的作用域链

当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,直至作为作用域链终点的全局执行环境。

在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。

function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);

在上面的例子中,compare 函数的作用域链包含两个变量对象:本地活动对象(包含 arguments 和 value1、value2)和全局变量对象。

闭包的常见用法

闭包在 JavaScript 中有很多常见的用法,比如模块模式、私有变量、回调函数等。

模块模式
var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(Counter.value()); // 0
Counter.increment();
Counter.increment();
console.log(Counter.value()); // 2
Counter.decrement();
console.log(Counter.value()); // 1

在上面的例子中,Counter 是一个模块,它包含三个公共方法:increment、decrement 和 value。这些方法可以访问私有变量 privateCounter 和私有函数 changeBy。

私有变量
function MyObject() {
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 特权方法
this.publicMethod = function() {
privateVariable++;
return privateFunction();
};
}

在上面的例子中,privateVariable 和 privateFunction 是私有的,只能通过特权方法 publicMethod 来访问。

模仿块级作用域

在 JavaScript 中,没有块级作用域的概念,这意味着在块语句中定义的变量实际上是在包含函数中而非语句中创建的。

块级作用域问题

function outputNumbers(count) {
for (var i = 0; i < count; i++) {
console.log(i);
}
console.log(i); // 计数
}

在上面的例子中,变量 i 是在 for 循环中定义的,但它在循环外部仍然可以访问。

使用函数表达式模仿块级作用域

通过使用函数表达式,我们可以模仿块级作用域。

function outputNumbers(count) {
(function() {
for (var i = 0; i < count; i++) {
console.log(i);
}
})();
console.log(i); // 导致错误!
}

在上面的例子中,我们在 for 循环外部定义了一个匿名函数,并立即调用它。这样,变量 i 就只在匿名函数内部有效,外部无法访问。

私有变量

JavaScript 中没有私有成员的概念,所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。

私有变量示例

function Person(name) {
this.getName = function() {
return name;
};
this.setName = function(value) {
name = value;
};
}
var person = new Person("Nicholas");
console.log(person.getName()); // "Nicholas"
person.setName("Greg");
console.log(person.getName()); // "Greg"

在上面的例子中,name 是一个私有变量,只能通过 getName 和 setName 方法来访问。

静态私有变量

通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法。

(function() {
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 构造函数
MyObject = function() {
};
// 公共/特权方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();

在上面的例子中,私有变量 privateVariable 和私有函数 privateFunction 是由所有实例共享的。因为特权方法是在原型上定义的,所以所有实例都使用同一个函数。

总结

函数表达式是 JavaScript 中定义函数的一种重要方式,它具有许多独特的特性和用法。通过本文的介绍,我们了解了函数表达式的基本概念、与函数声明的区别、递归的实现方式、闭包的应用、模仿块级作用域以及私有变量等概念。

掌握这些知识,可以帮助我们更好地理解和使用 JavaScript 中的函数表达式,从而编写出更加优雅和高效的代码。

希望本文能够帮助大家对函数表达式有更深入的理解和应用。在实际开发中,我们可以根据具体需求选择合适的函数定义方式,充分发挥函数表达式的优势。


最后,创作不易请允许我插播一则自己开发的“数规规-排五助手”(有各种趋势分析)小程序广告,感兴趣可以微信小程序体验放松放松,程序员也要有点娱乐生活,搞不好就中个排列五了呢?

感兴趣的可以微信搜索小程序“数规规-排五助手”体验体验!

如果觉得本文有用,欢迎点个赞+收藏+关注支持我吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/966380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

11.15模拟赛

t1 小 \(\mathrm{L}\) 有一串 \(n\) 块宝石的项链, 它可以顺时针看成一个圆环, 即第 1 块宝石与第 2 块和第 \(n\) 块宝石相邻, 第 2 块宝石与第 1 块和第 3 块宝石相邻, 以此类推。 但不幸的是, 项链的宝石破碎了。每…

2025 最新无缝钢管优质厂家推荐:国际测评认证 + 技术创新 + 全场景适配 + 服务保障综合榜单

引言 本榜单基于国际钢管行业协会(ISSF)最新测评数据编制,通过三大核心维度构建权威评价体系:技术实力(占比 40%)涵盖生产工艺先进性、定制化能力及检测设备精度;产品品质(占比 35%)依据力学性能、尺寸公差、…

西门子S7200_SMART仿真软件的使用(保姆级教程)

第一步:编写一个简单的程序用于测试 第二步:导出“.awl”格式的文件第三步:双击打开软件包里的“S7_200汉化版.exe” 第四步:输入密码“6596”并点击“确定” 第五步:选择与之对应的CPU型号 第六步:点击“程序…

2025年RS485红外线测温仪源头厂家权威推荐榜单:在线红外测温仪/20mA红外线测温仪/红外线测温仪变送器源头厂家精选

在工业自动化与智能制造的浪潮下,RS485红外测温仪因其信号传输稳定、抗干扰能力强且便于组网的优势,市场需求持续增长。选择合适的RS485接口、支持Modbus-RTU协议的红外测温仪,已成为工业现场实现可靠温度监控的关键…

P14508 猜数游戏 guess

每次通过一些特别的手法可以知道一个长度的区间内是否有东西。 那么设 \(f_i\) 为长度为 \(i\) 的区间最少花费,那么每次将其划分成两个区间,看其中需要确定一个的最小花费是什么,这是好做的(因为确定了一个区间就…

AMD Instinct MI50 通过llama.cpp 在 ROCm7.0.2上运行

关于网上传言MI50 ROCm7.0.2的性能提升了,这边做了下测试。 ROCm7.0.2安装方法: ROCm 7.0 Install for Mi50 32GB | Ubuntu 24.04 LTS : r/LocalLLaMA 这边系统使用的ubuntu22.04 ROCm安装 wget https://repo.radeon.…

如何成为高级的安卓逆向工程师 glm4.6

如何成为高级的安卓逆向工程师 glm4.6如何成为高级的安卓逆向工程师 ● Ill help you understand how to become a senior Android reverse engineer. Let me create a comprehensive roadmap for you. ● 成为高级安…

PyTorch实战(9)——从零开始实现Transformer - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

天津雅思培训机构排名2025,无老师国际/新通教育等优质机构,师资/口碑/提分率大PK

天津雅思培训机构排名2025,无老师国际/新通教育等优质机构,师资/口碑/提分率大PK随着留学申请热度的持续攀升,雅思成绩已成为学子们海外深造的“通行证”,天津地区雅思培训市场也随之蓬勃发展。专业的雅思培训机构…

2025 最新无缝钢管源头厂家推荐:国际测评认证 + 技术创新 + 全场景适配 + 服务保障综合榜单

引言 本榜单基于国际钢管行业协会(ISSF)最新测评数据编制,通过三大核心维度构建权威评价体系:技术实力(占比 40%)涵盖生产工艺先进性、定制化能力及检测设备精度;产品品质(占比 35%)依据力学性能、尺寸公差、…

用HBuilder建立查询天气的网页

查询天气.container { max-width: 500px; margin: 50px auto; padding: 20px; border: 1px solid rgba(221, 221, 221, 1); border-radius: 10px } .input-group { margin-bottom: 20px } input { padding: 8px; width…

2025 11 15

第23场弘文了这场我T1都没切,我考虑枚举排列和起点之后,我不知道为什么就没有去想把在A中的B和在B中的A率先交换,以及在A中的C和在C中的A交换,然后再算B,C的,理由应该归咎于状态问题加上没有充分思考,因为一开始…

常用设计模式:职责链模式

根据 ChatGPT 的描述, 后端项目中出现频率最高的前 6 名设计模式是:排名 模式 典型应用🥇 1 单例模式 数据库连接、缓存客户端、全局配置🥈 2 工厂模式 动态加载不同实现(如多租户、支付、存储)🥉 3 代理模式…

fanuc 双安检实验指导书

实验:双安检功能实验学时:2~3学时实验目的与要求了解并掌握双安检功能原理以及应用,通过该功能的学习能够确保信号输入输出的正确性、保证机床处于准确的安全位置以及定期进行安全测试。实验内容双安检功能硬件连接…

Rust RefCell 多线程读为什么也panic了?

Rust RefCell 多线程读为什么也panic了?这是最近实战中遇到的一个小知识点,没理解之前觉得「不可能」,反应过来之后,觉得自己很蠢🤣,借本文记录下。 看一段复现代码: struct MyRefCell<T>(RefCell<T&…

关于样式

请大家使用亮色观看,目前暗色模式有些功能未开发

Java-Spring入门指南(二十四)SSM整合HTML:克服CSS/JS静态资源被过滤问题

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

1115noip模拟赛

8:15 想到T1正解,写完了 9:00 T2一开始想了个假贪心,构造了一组小数据卡掉了 10:00 想到了一个dp做法,写完过了样例 10:30 想T3没想出来,只想到了一个 \(O(N^3)\) 做法 10:50 写完T3暴力之后,发现T4是谢昀均之前想…

2025 最新推荐!汽车喇叭网生产厂家权威排行榜,0.01MM 精度 + 全工艺保障,靠谱品牌甄选

本次榜单由国际汽车零部件品质测评协会(IAQP)联合精密制造技术联盟共同发布,基于全球 200 余家企业的实测数据生成。测评采用 “四维评分体系”:工艺精度(30%)、品质稳定性(25%)、产能交付(25%)、服务适配性…

2025年毕业论文救星:6款免费AI写论文工具实测推荐

校园论坛上学生毕业论文写作难题引发共鸣,作者结合自身经历,实测推荐 2025 年 6 款免费 AI 写论文工具,包括 AI 论文及时雨、PaperNex、图灵论文 AI 写作助手等。这些工具各有特色,均能快速生成初稿,支持多种论文…