Node.js 快速成为构建 Web 应用和系统软件的标准,得益于它能够在后端使用 JavaScript。流行框架如 Express 和工具如 Webpack 等也促成了它的广泛应用。尽管存在诸如 Deno 和 Bun 等竞争对手,Node 仍然是领先的服务器端 JavaScript 平台之一。
JavaScript的多范式特性允许使用各种编程风格,但也带来了诸如作用域和对象变更的风险。缺乏尾递归优化使得大的递归操作变得危险,并且Node.js的单线程特性需要使用异步代码来保持高效。尽管存在这些难题,遵循JavaScript的关键理念和最佳实践可以帮助Node.js开发人员写出可扩展且高效的代码。
zh: 1. JavaScript 闭包: 一个闭包是指一个函数与其声明时所在的词法环境的组合。
在JavaScript中,闭包是一个内部函数,即使外部函数已经返回,仍然可以访问外部函数的作用域。闭包使内部函数的变量保持私有。随着函数式编程的流行,闭包已经成为Node开发者工具箱中不可或缺的一部分。这里有一个简单的JavaScript闭包示例。
这是一张图片,你可以点击链接查看。
- 变量
count
被赋值给一个外部函数。外部函数仅运行一次,将计数器设置为零并返回一个内部函数。变量_counter
只能由内部函数访问,这使它表现得像一个私有的变量。 - 这里的例子是一个高阶函数(或元函数,即接受或返回另一个函数的函数)。闭包在许多其他应用场景中也可以找到。当在另一个函数内部定义一个函数时,并且该内部函数不仅拥有自己的作用域,还可以访问父作用域,就会发生闭包。也就是说,内部函数可以“访问”外部变量,而外部函数则不能访问内部函数的作用域。
- 在使用诸如
map(innerFunction)
这样的函数方法时,这也非常有用,因为innerFunction
可以使用在外部作用域中定义的变量。
2. JavaScript原型系统
每个 JavaScript 函数都有一个 prototype 属性,用于附加属性和方法到函数上。此属性是不可枚举的。它允许开发人员向其对象附加方法或成员函数。JavaScript 的继承只能通过 prototype 属性来实现。当对象继承自其他对象时,prototype 属性指向该对象的原型对象。通常会通过原型来添加方法到函数,如下所示:
这是一张图片:
尽管现代 JavaScript 已经具备相当复杂的类支持,它仍然在底层使用原型系统。这正是该语言灵活性的一个重要原因。
3. 使用哈希名称设定私有特性
在过去,用下划线开头的变量表示该变量是私有的。然而,这只是个建议性做法,并不是平台强制要求的限制。现代 JavaScript 提供了 # 私有成员和方法。
这是一张图片
私有属性名是 JavaScript 中一个较新且非常棒的新功能!最近的 Node.js 版本和大多数浏览器都已经支持这一功能,Chrome 的开发者工具还让你可以直接访问私有变量,方便得很。
4. 使用闭包来定义私有变量
另一种常见的方式是使用闭包来绕过JavaScript原型系统中缺乏私有成员的问题。现代JavaScript使用井号前缀来定义私有成员,如上例所示。但是,这种方法不适用于JavaScript原型系统中的私有成员定义。此外,这种技巧你经常会看到,理解它的工作原理是很重要的。
使用闭包定义私有属性可以让你模拟私有变量的效果。需要访问这些私有属性的成员函数应该直接定义在对象上。这里是使用闭包定义私有属性的语法:
zh: 5、JavaScript模块
很久以前,JavaScript 没有模块系统,开发人员设计了一个聪明的小技巧(称为模块模式)来搭建一个能用的系统。随着 JavaScript 的不断发展,它催生了不止一个,而是两个模块系统:CommonJS 的 include 用法和 ES6 require 语法。
节点(Node)传统上使用 CommonJS,而浏览器传统上使用的是另一种模块化方式(如 AMD 或者直接使用原生 ES6 模块,但在文本中没有明确说明,这里保持原翻译)。然而,最近的 Node 版本(例如,版本 10 及以上)也开始支持 ES6。现在大伙儿都开始倾向于使用 ES6 模块了,并且未来我们可能会在 JavaScript 中统一使用一种模块语法。ES6 的写法大致如下(例如,导出一个默认模块并进行导入):
(点击可以查看。)
你仍然会看到CommonJS,并且有时需要使用它来导入模块。CommonJS中的具体操作如下:如何导出和导入默认模块。因此,这里展示了使用CommonJS导出和导入默认模块的方法。
zh:6. 错误管理.
无论你处在什么样的语言环境,错误处理都是必要的。Node也一样。你会用到三种基本的方式来处理错误:try/catch结构、抛出新的异常以及on()事件处理器。
使用 try/catch 结构的代码块是用来捕捉错误的可靠方法,当出错时。
如图所示:图片描述
在这种情况下,我们会把错误用 console.error 记录到控制台。你可以选择抛出错误,让下一个处理程序接手。需要注意的是,这样做会中断代码执行流程;也就是说,当前执行会停止,然后栈中的下一个错误处理程序会接手。
现代 JavaScript 提供了多个有用的 Error 对象属性,比如可以用 Error.stack 来查看堆栈跟踪。在上面的例子中,我们用构造函数的参数设置了 Error.message。
你还会在异步代码块中遇到错误,特别是在处理正常结果时使用 .then()。在这种情况下,你可以使用 on('error') 处理程序或 onerror 事件,具体取决于 promise 如何传递错误信息。有时,API 会将错误对象作为第二个返回值返回,而正常值作为第一个返回值。(如果你在异步调用中使用 await,可以用 try/catch 包裹来捕获并处理任何错误。) 这是一个简单的异步错误处理示例:
不管怎样,千万别吞了错误!这里不展示,避免有人直接复制粘贴。总之,如果捕获了错误但没采取任何行动,你的程序会继续运行,毫无迹象显示出了问题。逻辑会出问题,你得琢磨为什么捕获块空着。(提示:只有finally{}块没有catch块,会导致错误被吞下。)
7. JavaScript 柯里化
柯里化是一种使函数更加灵活的方法。使用柯里化的函数,您可以传递所有期望的参数并得到结果,或者只传递部分参数,这样会返回一个等待其余参数的函数。下面是一个简单的柯里化示例:
原始的 curried 函数可以直接这样调用:每个参数用一组单独的括号括起来依次调用。
例如:“curriedFunction(a)(b)(c)”
这是一个有趣的技术,允许你创建函数生成器,在这种生成器中,外部函数可以部分地定制内部函数。比如,你可以这样使用上面提到的柯里化函数,比如:
- original code remains unchanged
在实际应用中,这个概念或思路当你需要创建许多随某些参数变化的功能时,会对你有所帮助。
8. JavaScript 的 apply、call 和 bind 方法
虽然我们不是每天都用到它们,但了解一下 call 、 apply 和 bind 方法是什么还是不错的。这里我们讨论的是语言的一些灵活性。说到底,这些方法让你可以指定 this 关键字指向哪个对象。
在这三个函数中,第一个参数始终是你希望传递给函数的 this 值或上下文环境。
在这三个中,打电话是最简单的。就像在特定环境下调用一个函数一样。举个例子,
注意 apply 与 call 几乎相同。唯一的区别在于,您需要将参数作为数组传递,而不是分别传递。在JavaScript中,数组更容易操作,这为处理函数提供了更多的可能性。下面是一个使用 apply 和 call 的示例:
,这是一张图片,你可以点击它查看详细内容。
bind
方法允许你向函数传递参数而无需调用它。返回一个新的函数,这些参数会被预先绑定,再接受其他的参数。例如:
9. JavaScript 缓存
记忆化是一种优化技术,通过存储昂贵操作的返回值来加速函数执行,并当相同的输入再次出现时返回缓存的结果。JavaScript对象的行为类似于关联数组,使在JavaScript中实现记忆化变得简单。这里是如何将一个递归阶乘函数转换为记忆化版的阶乘函数的方法:
这张图片展示的是...
10. JavaScript 立即执行函数(IIFE)
立即执行的函数表达式(IIFE)是一种在创建时立即执行的函数。它与特定事件或异步任务无关。你可以这样定义一个IIFE:
如图所示:
第一个括号对 function(){...} 会将括号内的代码封装为一个表达式。第二个括号对会调用该内部创建的函数。IIFE(立即执行函数表达式,也称自调用匿名函数)可被描述为一个立即执行的匿名函数。它的最常见用途是通过 var 关键字限制变量的作用范围,或封装上下文以避免变量命名冲突。
有时你也会遇到这种情况,需要调用带有 await 的函数,但你没有在一个异步函数内部。这种情况通常发生在你希望它可以直接执行的文件中,同时也可能被当作模块导入的文件中。可以将这样的函数调用包裹在一个立即执行函数表达式(IIFE)中,例如:
查看图片
11.: 有用的论据特点
尽管JavaScript不支持函数重载(因为它可以处理任意数量的函数参数),但它确实提供了几种强大的工具来处理参数。其中之一就是可以定义带有默认值的函数或方法:其中一个办法就是定义带有默认值的函数或方法。
(点击查看图片)
您也可以一次性接受并处理所有参数,这种方法使用了rest操作符将其收集到一个数组中,以便处理任意数量的参数传递。
如图所示:
如果你确实需要处理不同参数配置的情况,你可以检查这些参数配置。
另外,请记住JavaScript包含一个内置的arguments数组。每个函数或方法都会自动提供arguments变量,该变量包含了所有传递给该函数调用的参数。
zh: 结论
当你开始熟悉 Node 之后,你会注意到几乎每个问题都有很多解决方法。正确的方法有时候并不明显。有时候,面对某个问题可以有几种有效的解决方法。了解这些选择会对你有帮助。
这里提到的10个JavaScript概念每个Node.js开发者都应该知道,但这些概念只是冰山一角。JavaScript不仅强大,而且复杂。你用得越多,就越能理解JavaScript的广阔天地和你能用它做到的事情。