在本文中,我们将探讨一些最令人兴奋和期待的 JavaScript 特性,这些特性预计将在 2024 年推出。
以下提案很有可能被纳入今年的 ECMAScript 版本中:
-
Temporal (时间 API) -
Pipe Operator(管道操作) -
Records and Tuples(全新类型 Records 和元组) -
Regular Expressions /v
flag (正则表达式) -
Decorators(装饰器)
ECMAScript 更新
JavaScript 的新版本总是引起轰动。自 ES6 更新以来,每年都会有一个新版本,我们预计今年的(ES2024)将在 6 月左右推出。
ES6 是在其前身 ES5 六年后推出的一个大规模版本。浏览器供应商和 JavaScript 开发人员被大量新功能所淹没,需要花费大量时间来学习和采用。从那时起,为了防止一次添加大量新功能,采用了年度发布周期。
这个年度发布周期包括提出任何新功能,然后由委员会讨论、评估,然后投票决定是否将其添加到语言中。这个过程还允许浏览器在正式添加到语言之前尝试实现这些建议,这可能有助于解决任何实现问题。
如前所述,JavaScript(或 ECMAScript)的新功能由技术委员会 39 (TC39) 决定。TC39 由所有主要浏览器供应商以及 JavaScript 专家的代表组成。他们定期开会讨论语言的新功能以及如何实现它们。新功能以提案(由任何人提出)的形式提出,然后由委员会成员投票决定每个提案是否可以进入下一阶段。每个提案都有 4 个阶段;一旦提案达到第 4 阶段,它就有望包含在 ES 的下一个版本中。
ES 规范的一个重要部分是它必须向后兼容。这意味着任何新功能都不能通过更改以前版本的 ES 的工作方式来破坏互联网。因此,它们不能更改现有方法的工作方式,只能添加新方法,因为任何使用潜在先前方法运行的网站都有可能中断。
可以在此处(https://github.com/tc39/proposals/tree/main)查看所有当前提案的完整列表。
Temporal
在State of JS 2022 调查中,“你觉得 JavaScript 目前缺少什么?”这个问题的第三个最常见的答案是更好的日期管理。
这导致了Temporal
提案的出现,它提供了一个标准的全局对象来替代Date
对象,并修复了多年来在 JavaScript 中处理日期时给开发人员带来很多痛苦的一些问题。
在 JavaScript 中处理日期几乎总是一项令人生畏的任务;必须处理一些小但令人愤怒的不一致之处,例如月份从零索引但月份中的天数从 1 开始。
日期的难度导致了诸如Moment、Day.JS和date-fns等流行库的出现,试图解决这些问题。然而,Temporal
API 旨在原生修复所有问题。
Temporal
将支持多个时区和非格里高利历,并提供一个简单易用的 API,使从字符串解析日期变得更加容易。此外,所有Temporal
对象都是不可变的,这将有助于避免任何意外的日期更改错误。
让我们看一些示例,这些示例展示了Temporal
API 提供的一些最有用的方法。
Temporal.Now.Instant()
Temporal.Now.Instant()
将返回最接近纳秒的DateTime
对象。可以使用from
方法指定特定日期,如下所示:
const olympics = Temporal.Instant.from('2024-07-26T20:24:00+01:00');
这将创建一个表示今年稍后于 20:24 开始的巴黎奥运会的DateTime
对象(UTC)。
PlainDate()
这允许你创建一个只有日期而没有时间的日期:
new Temporal.PlainDate(2024, 7, 26);
Temporal.PlainDate.from('2024-07-26');
// 都返回一个表示 2024 年 7 月 26 日的 PlainDate 对象
PlainTime()
作为PlainDate()
的补充,我们可以使用它来创建只有时间而没有日期的时间,使用.PlainTime()
:
new Temporal.PlainTime(20, 24, 0);
Temporal.PlainTime.from('20:24:00');
// 都返回一个表示 20:24 的 PlainTime 对象
PlainMonthDay()
PlainMonthDay()
与PlainDate()
类似,但它只返回月份和日期,没有年份信息(对于每年在同一天重复的日期很有用,例如圣诞节和情人节):
const valentinesDay = Temporal.PlainMonthDay.from({ month: 2, day: 14 });
PlainYearMonth()
同样,也有PlainYearMonth
,它只返回年份和月份(对于表示全年的整个月份很有用):
const march = Temporal.PlainYearMonth.from({ month: 3, year: 2024 });
Calculations
可以使用Temporal
对象进行许多计算。你可以向日期对象添加和减去不同单位的时间:
const today = Temporal.Now.plainDateISO();
const lastWeek = today.subtract({ days: 7 });
const nextWeek = today.add({ days: 7 });
until
和since
方法可用于查找距离特定日期还有多少时间或自该日期以来经过了多少时间。例如,以下代码将告诉你距离巴黎奥运会还有多少天:
olympics.until().days
valentinesDay.since().hours
这些方法返回一个Temporal.Duration
对象,可用于测量具有许多不同单位和舍入选项的时间量。
其它用法
你可以从 Date
对象中提取年份、月份和日期,以及从 Time
对象中提取小时、分钟、秒、毫秒、微秒和纳秒(微秒和纳秒在当前的 DateTime
对象中不可用)。例如:
olympics.hour;
<< 20
还有其他属性,例如 dayOfWeek
(返回值为 1 表示星期一,返回值为 7 表示星期日),daysInMonth
(根据月份返回 28、29、30 或 31)和 daysInYear
(根据闰年返回 365 或 366)。
Temporal
日期对象还将具有 compare
方法,可用于使用各种排序算法对日期进行排序。
Temporal
目前是第 3 阶段的提议,正在由浏览器供应商实施,所以它似乎已经到来(双关语)。你可以在此处查看完整文档 https://tc39.es/proposal-temporal/docs/。此处还有一个有用的用例手册 https://tc39.es/proposal-temporal/docs/cookbook.html。当与 Intl.DateTimeFormat API 结合使用时,你将能够进行一些非常巧妙的日期操作。
Pipe Operator
在 2022 年的 JavaScript 现状调查中,对于“你认为 JavaScript 目前缺少什么?”这个问题,第六个最受欢迎的答案是 Pipe Operator。
你可以在这里查看 Pipe Operator 的提议:https://github.com/tc39/proposal-pipeline-operator。
Pipe Operator 是函数式语言的标准功能,它允许你将一个函数的输出作为输入传递给下一个函数(类似于 Fetch API 将从一个 Promise 返回的数据传递给下一个)。
例如,假设我们要对一个字符串连续应用三个函数:
-
在原始字符串的开头添加字符串“Listen up!”。 -
在字符串的末尾添加三个感叹号。 -
将所有文本转换为大写。
这三个函数可以这样写:
const exclaim = (string) => string + "!!!";
const listen = (string) => "Listen up! " + string;
const uppercase = (string) => string.toUpperCase();
这三个函数可以通过将它们嵌套在一起来应用:
const text = "Hello World";
uppercase(exclaim(listen(text))) << "LISTEN UP! HELLO WORLD!!!";
但是,像这样深度嵌套多个函数调用会很快变得混乱,特别是作为参数传递的值(text
)最终会深深嵌入到表达式中,使得很难识别。
函数嵌套的另一个问题是函数的应用顺序是反向的,也就是说,最内部的函数首先应用。所以在这种情况下,listen
函数首先应用于 text
的原始值,然后是 exclaim
,最后是最外部的函数 uppercase
。特别是对于大型和复杂的函数,这变得难以理解和遵循。
另一种选择是使用函数链接,如下所示:
const text = "Hello World";
text.listen().exclaim().uppercase();
这解决了嵌套函数的许多问题。传递的参数在开头,并且每个函数按照应用的顺序出现,因此listen()
首先应用,然后是exclaim()
,然后是uppercase()
。
不幸的是,这个示例不会起作用,因为 listen
、exclaim
和 uppercase
函数不是 String
类的方法。它们可以被添加 monkey patching String
类, 但是这种技术通常不被推荐。
这意味着,尽管链式调用看起来比函数嵌套好多了,但它实际上只能与内置函数一起使用(就像经常使用数组方法一样)。
Piping 结合了连锁的易用性,但能够与任何函数一起使用。根据当前的提议,上面的示例将这样编写:
plaintext
text |> listen(%) |> exclaim(%) |> uppercase(%)
%
是一个占位符,用于表示前一个函数的输出值,尽管%
字符很可能在正式发布中被其他字符替换。这允许在管道中使用接受多个参数的函数。
Piping 结合了连锁的易用性,但可以与你编写的任何自定义函数一起使用。唯一的条件是你需要确保一个函数的输出类型与链中的下一个函数的输入类型匹配。
Piping 最适合使用 curried functions,这些函数只接受一个从任何先前函数的返回值中管道的参数。它使函数式编程变得更加容易,因为可以将小型的构建块函数链接在一起来创建更复杂的复合函数。它还使 partial application 更容易实现。
尽管很受欢迎,但管道运算符一直难以推进到流程的第二阶段之后。这是由于对符号表示方式的分歧以及对内存性能的担忧以及它如何与 await
一起工作。看起来委员会正在慢慢达成某种协议,所以希望管道运算符能够快速通过各个阶段并在今年亮相。
值得庆幸的是,管道运算符已在 Babel 的 7.15 版本中实现。
就个人而言,我们希望管道运算符能够得到实现并在今年推出,因为它将真正有助于提高 JavaScript 作为一种严肃的函数式编程语言的信誉。
记录和元组
记录和元组提案旨在将不可变数据结构引入 JavaScript。
元组类似于数组——一个有序的值列表——但它们是深度不可变的。这意味着元组中的每个值都必须是原始值或另一个记录或元组(不是数组或对象,因为它们在 JavaScript 中是可变的)。
元组的创建方式与数组字面量类似,但在前面加了一个哈希符号(#
):
const heroes = #["Batman", "Superman", "Wonder Woman"]
创建后,不能添加其他值,也不能删除任何值。这些值也不能更改。
记录类似于对象——键值对的集合——但它们也是深度不可变的。它们的创建方式与对象类似——但与元组一样,它们也以哈希符号开头:
const traitors = #{
diane: false,
paul: true,
zac: false,
harry: true
}
记录仍将使用点标记法来访问属性和方法:
traitors.paul
<< true
而数组使用的方括号标记法也可用于元组:
heroes[1]
<< "Superman"
但是由于它们是不可变的,你不能更新任何属性:
traitors.paul = false
<< Error
heroes[1] = "Supergirl"
<< Error
元组和记录的不可变性意味着你可以使用===
运算符轻松地对它们进行比较:
heroes === #["Batman", "Superman", "Wonder Woman"];
<< true
需要注意的是,在考虑记录的相等性时,属性的顺序并不重要:
traitors === #{
ross: false,
zac: false,
paul: true,
harry: true
};
// 仍然是 true,即使人员的顺序已经改变
<< true
但是,对于元组,顺序很重要,因为它们是一个有序的数据列表:
heroes === #["Wonder Woman", "Batman", "Superman"];
<< false
这个页面有一个方便的教程,带有一个实时操场,因此你可以习惯记录和元组的工作方式。
正则表达式的 /v
标志
自 JavaScript 版本 3 以来,正则表达式已被集成到 JavaScript 中,此后进行了许多改进(例如,在 ES2015 中使用 u
标志进行 Unicode 支持)。/v
标志提案旨在实现 u
标志的所有功能,但它增加了一些额外的好处,我们将在下面的示例中看到。
简单地说,实现 v
标志需要在正则表达式的末尾添加 /v
。
例如,以下代码可用于测试字符是否为表情符号:
plaintext
const isEmoji = /^p{RGI_Emoji}$/v;
isEmoji.test("💚");
<< true
isEmoji.test("🐨");
<< true
这使用 RGI_Emoji
模式来识别表情符号。
v
标志还允许你在正则表达式中使用集合表示法。例如,你可以使用 --
运算符从一个模式中减去另一个模式。以下代码可用于从表情符号集中删除任何爱心:
plaintext
const isNotHeartEmoji = /^[p{RGI_Emoji_Tag_Sequence}--q{💜💚♥️💙🖤💛🧡🤍🤎}]$/v;
isNotHeartEmoji.test("💚");
<< false
isNotHeartEmoji.test("🐨");
<< true
你可以使用 &&
找到两个模式的交集。例如,以下代码将找到希腊符号和字母的交集:
plaintext
const GreekLetters = /[p{Script_Extensions=Greek}&&p{Letter}]/v;
GreekLetters.test('π');
<< true
GreekLetters.test('𐆊');
<< false
v
标志还解决了 u
标志在大小写不敏感方面的一些问题,使其成为几乎在所有情况下都更好的选择。
正则表达式的 v
标志在 2023 年达到了第 4 阶段,并已在所有主要浏览器中实现,因此它完全有望成为 ES2024 规范的一部分。
装饰器
装饰器提案旨在使用装饰器原生地扩展 JavaScript 类。
装饰器在许多面向对象的语言(如 Python)中已经很常见,并且已经包含在 TypeScript 中。它们是一种标准的元编程抽象,允许你向函数或类添加额外的功能,而无需更改其结构。例如,你可能希望向方法添加一些额外的验证,你可以通过创建一个验证装饰器来检查输入到表单的数据。
虽然 JavaScript 允许你使用函数来实现这种设计模式,但大多数面向对象的程序员更喜欢一种更简单、更原生的方式来实现这一点,只是为了让生活更轻松。
该提案添加了一些语法糖,以便你可以轻松地在类内部实现装饰器,而无需考虑将 this
绑定到类。它提供了一种更清洁的方式来扩展类元素,例如类字段、类方法或类访问器,甚至可以将其应用于整个类。
装饰器由 @
符号标识,并始终放置在它们正在“装饰”的代码之前。
例如,类装饰器将立即出现在类定义之前。在下面的示例中,validation
装饰器应用于整个 FormComponent
类:
@validation
class FormComponent {
// 这里是类代码
}
// 还需要定义装饰器函数
function validation(target) {
// 验证代码在这里
}
类方法装饰器将立即出现在它装饰的方法之前。在下面的示例中,validation
装饰器应用于 submit
方法:
class FormComponent {
// 这里是类代码
@validation
submit(data) {
// 这里是方法代码
}
}
// 还需要定义装饰器函数
function validation(target) {
// 验证代码在这里
}
装饰器函数定义接受两个参数:值和上下文。值参数是指正在装饰的值(例如,类方法),上下文包含有关该值的元数据,例如它是否是函数、其名称以及它是静态的还是私有的。你还可以向上下文添加一个初始化函数,该函数将在类实例化时运行。
装饰器提案目前处于第 3 阶段,并已在 Babel 中实现,因此你已经可以尝试一下。
总结
那么你觉得呢?你希望今年在规范中添加什么内容?所有这些功能都将为 JavaScript 带来巨大的补充,所以祈祷它们能在今年实现!
翻译自: https://www.sitepoint.com/new-javascript-ecmascript/
点击下方关注公众号,每天为你带来前端最新资讯及优质前端技术文章
原文始发于微信公众号(web前端进阶):2024 年 JavaScript 的 5 个令人兴奋的新特性
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/255138.html