美橙建站之星怎么样,汕头投资建设总公司网站,交通建设门户网站,3d建模教程人物大家好#xff0c;我是若川。持续组织了5个月源码共读活动#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与#xff0c;每周大家一起学习200行左右的源码#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。ECMAScript 规范每… 大家好我是若川。持续组织了5个月源码共读活动感兴趣的可以点此加我微信 ruochuan12 参与每周大家一起学习200行左右的源码共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。ECMAScript 规范每年都会更新一次正式标准化 JavaScript 语言的 ECMAScript 的下一次年度更新将在 2022 年 6 月左右获得批准。自 2015 年以来TC39 团队成员每年都会一起讨论可用的提案并发布已接受的提案。今年是 ECMAScript 的第 13 版其中包括许多实用的功能。所有在 2022 年 3 月之前达到阶段 4 的提案都将包含在全新的 ECMAScript 2022 标准中。对于一个提案从提出到最后被纳入ES新特性TC39的规范中分为五步stage0strawman任何TC39的成员都可以提交。stage1proposal进入此阶段就意味着这一提案被认为是正式的了需要对此提案的场景与API进行详尽的描述。stage2draft演进到这一阶段的提案如果能最终进入到标准那么在之后的阶段都不会有太大的变化因为理论上只接受增量修改。state3candidate这一阶段的提案只有在遇到了重大问题才会修改规范文档需要被全面的完成。state4finished这一阶段的提案将会被纳入到ES每年发布的规范之中目前一些提案还处于第三阶段一些提案已经进入第四阶段。提案的功能将在达到第 4 阶段后被添加到新的ECMAScript标准中这意味着它们已获得 TC-39 的批准通过了测试并且至少有两个实现。下面就来看看 ECMAScript 2022 预计会推出的新功能吧~一、Top-level Await 在ES2017中引入了 async 函数和 await 关键字以简化 Promise 的使用但是 await 关键字只能在 async 函数内部使用。尝试在异步函数之外使用 await 就会报错SyntaxError - SyntaxError: await is only valid in async function。顶层 await 允许我们在 async 函数外面使用 await 关键字目前提案正处于第 4 阶段并且已经在三个主要的浏览器的 JavaScript 引擎中实现模块系统会协调所有的异步 promise。顶层 await 允许模块充当大型异步函数通过顶层 await这些ECMAScript模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。下面来看一个简单的例子。由于 await 仅在 async 函数中可用因此模块可以通过将代码包装在 async 函数中来在代码中包含 await// a.jsimport fetch from node-fetch;let users;export const fetchUsers async () {const resp await fetch(https://jsonplaceholder.typicode.com/users);users resp.json();}fetchUsers();export { users };// usingAwait.jsimport {users} from ./a.js;console.log(users: , users);console.log(usingAwait module);我们还可以立即调用顶层async函数IIAFEimport fetch from node-fetch;(async () {const resp await fetch(https://jsonplaceholder.typicode.com/users);users resp.json();})();export { users };这样会有一个缺点直接导入的 users 是 undefined需要在异步执行完成之后才能访问它// usingAwait.js
import {users} from ./a.js;console.log(users:, users); // undefinedsetTimeout(() {console.log(users:, users);
}, 100);console.log(usingAwait module);当然这种方法并不安全因为如果异步函数执行花费的时间超过100毫秒 它就不会起作用了users 仍然是 undefined。另一个方法是导出一个 promise让导入模块知道数据已经准备好了//a.js
import fetch from node-fetch;
export default (async () {const resp await fetch(https://jsonplaceholder.typicode.com/users);users resp.json();
})();
export { users };//usingAwait.js
import promise, {users} from ./a.js;
promise.then(() { console.log(usingAwait module);setTimeout(() console.log(users:, users), 100);
});虽然这种方法似乎是给出了预期的结果但是有一定的局限性导入模块必须了解这种模式才能正确使用它。而顶层await就可以消除这些缺点// a.jsconst resp await fetch(https://jsonplaceholder.typicode.com/users);const users resp.json();export { users};// usingAwait.jsimport {users} from ./a.mjs;console.log(users);console.log(usingAwait module);顶级 await 在以下场景中将非常有用动态加载模块const strings await import(/i18n/${navigator.language});资源初始化const connection await dbConnector();依赖回退let translations;
try {translations await import(https://app.fr.json);
} catch {translations await import(https://fallback.en.json);
}目前在这些地方已经支持 Top-level awaitV8 v8.9Webpack 5.0.0BabelChrome DevTools REPLNode REPL二、类的实例成员 1. 公共实例字段公共类字段允许我们使用赋值运算符 () 将实例属性添加到类定义中。下面来一个计数器的例子import React, { Component } from react;export class Incrementor extends Component {constructor() {super();this.state {count: 0,};this.increment this.increment.bind(this);}increment() {this.setState({ count: this.state.count 1 });}render() {return (button onClick{this.increment}Increment: {this.state.count}/button);}
}在这个例子中在构造函数中定义了实例字段和绑定方法通过新的类语法我们可以使代码更加直观。新的公共类字段语法允许我们直接将实例属性作为属性添加到类上而无需使用构造函数方法。这样就简化了类的定义使代码更加简洁、可读import React from react;export class Incrementor extends React.Component {state { count: 0 };increment () this.setState({ count: this.state.count 1 });render () (button onClick{this.increment}Increment: {this.state.count}/button);
}有些小伙伴可能就疑问了这个功能很早就可以使用了呀。但是它现在还不是标准的 ECMAScript默认是不开启的如果使用 create-react-app 创建 React 项目那么它默认是启用的否则我们必须使用正确的babel插件才能正常使用babel/preset-env。下面来看看关于公共实例字段的注意事项公共实例字段存在于每个创建的类实例上。它们要么是在Object.defineProperty()中添加要么是在基类中的构造时添加构造函数主体执行之前执行要么在子类的super()返回之后添加class Incrementor {count 0
}const instance new Incrementor();
console.log(instance.count); // 0未初始化的字段会自动设置为 undefinedclass Incrementor {count
}const instance new Incrementor();
console.assert(instance.hasOwnProperty(count));
console.log(instance.count); // undefined可以进行字段的计算const PREFIX main;class Incrementor {[${PREFIX}Count] 0
}const instance new Incrementor();
console.log(instance.mainCount); // 02. 私有实例字段、方法和访问器默认情况下ES6 中所有属性都是公共的可以在类外检查或修改。下面来看一个例子class TimeTracker {name zhangsan;project blog;hours 0;set addHours(hour) {this.hours hour;}get timeSheet() {return ${this.name} works ${this.hours || nothing} hours on ${this.project};}
}let person new TimeTracker();
person.addHours 2; // 标准 setter
person.hours 4; // 绕过 setter 进行设置
person.timeSheet;可以看到在类中没有任何措施可以防止在不调用 setter 的情况下更改属性。而私有类字段将使用哈希#前缀定义从上面的示例中我们可以修改它以包含私有类字段以防止在类方法之外更改属性class TimeTracker {name zhangsan;project blog;#hours 0; // 私有类字段set addHours(hour) {this.#hours hour;}get timeSheet() {return ${this.name} works ${this.#hours || nothing} hours on ${this.project};}
}let person new TimeTracker();
person.addHours 4; // 标准 setter
person.timeSheet // zhangsan works 4 hours on blog当我们尝试在 setter 方法之外修改私有类字段时就会报错person.hours 4 // Error Private field #hours must be declared in an enclosing class我们还可以将方法或 getter/setter 设为私有只需要给这些方法名称前面加#即可class TimeTracker {name zhangsan;project blog;#hours 0; // 私有类字段set #addHours(hour) {this.#hours hour;}get #timeSheet() {return ${this.name} works ${this.#hours || nothing} hours on ${this.project};}constructor(hours) {this.#addHours hours;console.log(this.#timeSheet);}
}let person new TimeTracker(4); // zhangsan works 4 hours on blog由于尝试访问对象上不存在的私有字段会发生异常因此需要能够检查对象是否具有给定的私有字段。可以使用 in 运算符来检查对象上是否有私有字段class Example {#fieldstatic isExampleInstance(object) {return #field in object;}
}查看更多公有和私有字段提案信息https://github.com/tc39/proposal-class-fields3. 静态公共字段在ES6中不能在类的每个实例中访问静态字段或方法只能在原型中访问。ES 2022 将提供一种在 JavaScript 中使用 static 关键字声明静态类字段的方法。下面来看一个例子class Shape {static color blue;static getColor() {return this.color;}getMessage() {return color${this.color} ;}
}我们可以从类本身访问静态字段和方法console.log(Shape.color); // blueconsole.log(Shape.getColor()); // blueconsole.log(color in Shape); // trueconsole.log(getColor in Shape); // trueconsole.log(getMessage in Shape); // false实例不能访问静态字段和方法const shapeInstance new Shape();console.log(shapeInstance.color); // undefinedconsole.log(shapeInstance.getColor); // undefinedconsole.log(shapeInstance.getMessage());// colorundefined静态字段只能通过静态方法访问console.log(Shape.getColor()); // blue
console.log(Shape.getMessage()); //TypeError: Shape.getMessage is not a function这里的 Shape.getMessage() 就报错了这是因为 getMessage 不是一个静态函数所以它不能通过类名 Shape 访问。可以通过以下方式来解决这个问题getMessage() {return color${Shape.color} ;
}静态字段和方法是从父类继承的class Rectangle extends Shape { }console.log(Rectangle.color); // blue
console.log(Rectangle.getColor()); // blue
console.log(color in Rectangle); // true
console.log(getColor in Rectangle); // true
console.log(getMessage in Rectangle); // false4. 静态私有字段和方法与私有实例字段和方法一样静态私有字段和方法也使用哈希 (#) 前缀来定义class Shape {static #color blue;static #getColor() {return this.#color;}getMessage() {return color${Shape.#getColor()} ;}
}
const shapeInstance new Shape();
shapeInstance.getMessage(); // colorblue私有静态字段有一个限制只有定义私有静态字段的类才能访问该字段。这可能在我们使用 this 时导致出乎意料的情况class Shape {static #color blue;
static #getColor() {return this.#color;
}
static getMessage() {return color${this.#color} ;
}
getMessageNonStatic() {return color${this.#getColor()} ;
}
}class Rectangle extends Shape {}console.log(Rectangle.getMessage()); // Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it
const rectangle new Rectangle();
console.log(rectangle.getMessageNonStatic()); // TypeError: Cannot read private member #getColor from an object whose class did not declare it在这个例子中this 指向的是 Rectangle 类它无权访问私有字段 #color。当我们尝试调用 Rectangle.getMessage() 时它无法读取 #color 并抛出了 TypeError。可以这样来进行修改class Shape {static #color blue;static #getColor() {return this.#color;}static getMessage() {return ${Shape.#color};}getMessageNonStatic() {return color${Shape.#getColor()} color;}
}class Rectangle extends Shape {}
console.log(Rectangle.getMessage()); // colorblue
const rectangle new Rectangle();
console.log(rectangle.getMessageNonStatic()); // colorblue静态字段目前是比较稳定的并且提供了各种实现5. 类静态初始化块静态私有和公共字段只能让我们在类定义期间执行静态成员的每个字段初始化。如果我们需要在初始化期间像 try … catch 一样进行异常处理就不得不在类之外编写此逻辑。该提案就提供了一种在类声明/定义期间评估静态初始化代码块的优雅方法可以访问类的私有字段。先来看一个例子class Person {static GENDER Malestatic TOTAL_EMPLOYED;static TOTAL_UNEMPLOYED;try {// ...} catch {// ...}
}上面的代码就会引发错误可以使用类静态块来重构它只需将try...catch包裹在 static 中即可class Person {static GENDER Malestatic TOTAL_EMPLOYED;static TOTAL_UNEMPLOYED;static {try {// ...} catch {// ...}}
}此外类静态块提供对词法范围的私有字段和方法的特权访问。这里需要在具有实例私有字段的类和同一范围内的函数之间共享信息的情况下很有用。let getData;class Person {#xconstructor(x) {this.#x { data: x };}static {getData (obj) obj.#x;}
}function readPrivateData(obj) {return getData(obj).data;
}const john new Person([2,4,6,8]);readPrivateData(john); // [2,4,6,8]这里Person 类与 readPrivateData 函数共享了私有实例属性。三、Temporal JavaScript 中的日期处理 Date() 对象一直是饱受诟病该对象是1995 年受到 Java 的启发而实现的自此就一直没有改变过。虽然Java已经放弃了这个对象但是 Date() 仍保留在 JavaScript 中来实现浏览器的兼容。Date() API 存在的问题只支持UTC和用户的PC时间不支持公历以外的日历字符串到日期解析容易出错Date 对象是可变的比如const today new Date();
const tomorrow new Date(today.setDate(today.getDate() 1));console.log(tomorrow);
console.log(today);此时两个时间输出是一样的不符合我们的预期。正因为 Date() 对象存在的种种问题。平时我们经常需要借助moment.js、Day.js等日期库但是它们的体积较大有时一个简单的日期处理就需要引入一个库得不偿失。目前由于Date API 在很多库和浏览器引擎中的广泛使用没有办法修复API的不好的部分。而改变Date API 的工作方式也很可能会破坏许多网站和库。正因如此TC39提出了一个全新的用于处理日期和时间的标准对象和函数——Temporal。新的Temporal API 提案旨在解决Date API的问题。它为 JavaScript 日期/时间操作带来了以下修复仅可以创建和处理不可变Temporal对象提供用于日期和时间计算的简单 API支持所有时区从 ISO-8601 格式进行严格的日期解析支持非公历。Temporal 将取代 Moment.js 之类的库这些库很好地填补了 JavaScript 中的空白这种空白非常普遍因此将功能作为语言的一部分更有意义。由于该提案还未正式发布所以可以借助官方提供的prlyfill来测试。首选进行安装npm install js-temporal/polyfill导入并使用import { Temporal } from js-temporal/polyfill;console.log(Temporal);Temporal 对象如下下面就来看看 Temporal 对象有哪些实用的功能。1. 当前时间和日期Temporal.Now 会返回一个表示当前日期和时间的对象// 自1970年1月1日以来的时间秒和毫秒
Temporal.Now.instant().epochSeconds;
Temporal.Now.instant().epochMilliseconds;// 当前位置的时间
Temporal.Now.zonedDateTimeISO();// 当前时区
Temporal.Now.timeZone();// 指定时区的当前时间
Temporal.Now.zonedDateTimeISO(Europe/London);2. 实例时间和日期Temporal.Instant 根据 ISO 8601 格式的字符串返回一个表示日期和时间的对象结果会精确到纳秒Temporal.Instant.from(2022-02-01T05:56:78.99999999902:00[Europe/Berlin]);
// 输出结果2022-02-01T03:57:18.999999999Z
Temporal.Instant.from(2022-02-011T05:0607:00);
// 输出结果2022-01-31T22:06:00Z除此之外我们还可以获取纪元时间的对应的日期UTC 1970年1月1日0点是纪元时间Temporal.Instant.fromEpochSeconds(1.0e8);
// 输出结果1973-03-03T09:46:40Z3. 时区日期和时间Temporal.ZonedDateTime 返回一个对象该对象表示在特定时区的日期/时间new Temporal.ZonedDateTime(1234567890000, // 纪元时间Temporal.TimeZone.from(Europe/London), // 时区Temporal.Calendar.from(iso8601) // 默认日历
);Temporal.ZonedDateTime.from(2025-09-05T02:55:0002:00[Africa/Cairo]);Temporal.Instant(2022-08-05T20:06:1305:45).toZonedDateTime(05:45);
// 输出结果Temporal.ZonedDateTime.from({timeZone: America/New_York,year: 2025,month: 2,day: 28,hour: 10,minute: 15,second: 0,millisecond: 0,microsecond: 0,nanosecond: 0
});
// 输出结果2025-02-28T10:15:00-05:00[America/New_York]4. 简单的日期和时间我们并不会总是需要使用精确的时间因此 Temporal API 提供了独立于时区的对象。这些可以用于更简单的活动。Temporal.PlainDateTime指日历日期和时间Temporal.PlainDate指特定的日历日期Temporal.PlainTime指一天中的特定时间Temporal.PlainYearMonth指没有日期成分的日期例如“2022 年 2 月”Temporal.PlainMonthDay指没有年份的日期例如“10 月 1 日”。它们都有类似的构造函数以下有两种形式来创建简单的时间和日期new Temporal.PlainDateTime(2021, 5, 4, 13, 14, 15);
Temporal.PlainDateTime.from(2021-05-04T13:14:15);new Temporal.PlainDate(2021, 5, 4);
Temporal.PlainDate.from(2021-05-04);new Temporal.PlainTime(13, 14, 15);
Temporal.PlainTime.from(13:14:15);new Temporal.PlainYearMonth(2021, 4);
Temporal.PlainYearMonth.from(2019-04);new Temporal.PlainMonthDay(3, 14);
Temporal.PlainMonthDay.from(03-14);5. 日期和时间值所有 Temporal 对象都可以返回特定的日期/时间值。例如使用ZonedDateTimeconst t1 Temporal.ZonedDateTime.from(2025-12-07T03:24:3002:00[Africa/Cairo]);t1.year; // 2025
t1.month; // 12
t1.day; // 7
t1.hour; // 3
t1.minute; // 24
t1.second; // 30
t1.millisecond; // 0
t1.microsecond; // 0
t1.nanosecond; // 0其他有用的属性包括dayOfWeek周一为 1 至周日为 7dayOfYear1 至 365 或 366weekOfYear1 到 52有时是 53daysInMonth28、29、30、31daysInYear365 或 366inLeapYeartrue或false6. 比较和排序日期所有 Temporal 对象都可以使用 compare() 返回整数的函数进行比较。例如比较两个ZonedDateTime对象Temporal.ZonedDateTime.compare(t1, t2);这个比较结果会有三种情况当两个时间值相等时返回 0当 t1 在 t2 之后时返回 1当 t1 在 t2 之前时但会 -1const date1 Temporal.Now,
const date2 Temporal.PlainDateTime.from(2022-05-01);Temporal.ZonedDateTime.compare(date1, date2); // -1compare() 的结果可以用于数组的 sort() 方法来对时间按照升序进行排列从早到晚const t [2022-01-01T00:00:0000:00[Europe/London],2022-01-01T00:00:0000:00[Africa/Cairo],2022-01-01T00:00:0000:00[America/New_York]
].map(d Temporal.ZonedDateTime.from(d)).sort(Temporal.ZonedDateTime.compare);7. 日期计算提案还提供了几种方法来对任何 Temporal 对象执行日期计算。当传递一个Temporal.Duration对象时它们都会返回一个相同类型的新的 Temporal该对象使用years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds 和 nanoseconds 字段来设置时间。const t1 Temporal.ZonedDateTime.from(2022-01-01T00:00:0000:00[Europe/London]);t1.add({ hours: 8, minutes: 30 }); // 往后8小时30分t1.subtract({ days: 5 }); // 往前5天t1.round({ smallestUnit: month }); // 四舍五入到最近的月份until() 和 since() 方法会返回一个对象该 Temporal.Duration 对象描述基于当前日期/时间的特定日期和时间之前或之后的时间例如t1.until().months; // 到t1还有几个月t2.until().days; // 到t2还有几天t3.since().weeks; // t3已经过去了几周equals() 方法用来确定两个日期/时间值是否相同const d1 Temporal.PlainDate.from(2022-01-31);
const d2 Temporal.PlainDate.from(2023-01-31);
d1.equals(d2); // false8. 使用国际化 API 格式化日期虽然这不是 Temporal API 的一部分但 JavaScript Intl国际化API提供了一个 DateTimeFormat() 构造函数可以用于格式化 Temporal 或 Date 对象const d new Temporal.PlainDate(2022, 3, 14);// 美国日期格式3/14/2022
new Intl.DateTimeFormat(en-US).format(d);// 英国日期格式14/3/2022
new Intl.DateTimeFormat(en-GB).format(d);// 西班牙长日期格式miércoles, 14 de abril de 2022
new Intl.DateTimeFormat(es-ES, { dateStyle: full }).format(d);附TC39 关于 Temporal 的提案进度https://tc39.es/proposal-temporal/Chrome 关于 Temporal 的实现进度https://chromestatus.com/feature/5668291307634688#details四、内置对象 1. Object.hasOwn()在ES2022之前可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。提案中的 Object.hasOwn 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法。const example {property: 123
};console.log(Object.prototype.hasOwnProperty.call(example, property));
console.log(Object.hasOwn(example, property));2. at()at() 是一个数组方法用于通过给定索引来获取数组元素。当给定索引为正时这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时就会从数组的最后一项开始检索const array [0,1,2,3,4,5];console.log(array[array.length-1]); // 5
console.log(array.at(-1)); // 5console.log(array[array.lenght-2]); // 4
console.log(array.at(-2)); // 4除了数组字符串也可以使用at()方法进行索引const str hello world;console.log(str[str.length - 1]); // d
console.log(str.at(-1)); // d3. cause在 ECMAScript 2022 提案中new Error() 中可以指定导致它的原因function readFiles(filePaths) {return filePaths.map((filePath) {try {// ···} catch (error) {throw new Error(While processing ${filePath},{cause: error});}});
}4. 正则表达式匹配索引这个新提案已经进入第 4 阶段它将允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前我们只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下这是不够的。因此在这个新提案中如果设置标志 /d将额外获得一个带有开始和结束索引的数组。const matchObj /(a)(b)/d.exec(aaaabb);console.log(matchObj[1]) // aaaa
console.log(matchObj[2]) // bb由于 /d 标识的存在matchObj还有一个属性.indices它用来记录捕获的每个编号组console.log(matchObj.indices[1]) // [0, 4]
console.log(matchObj.indices[2]) // [4, 6]我们还可以使用命名组const matchObj /(?asa)(?bsb)/d.exec(aaaabb);console.log(matchObj.groups.as); // aaaa
console.log(matchObj.groups.bs); // bb这里给两个字符匹配分别命名为as和bs然后就可以通过groups来获取到这两个命名分别匹配到的字符串。它们的索引存储在 matchObj.indices.groups 中console.log(matchObj.indices.groups.as); // [0, 4]
console.log(matchObj.indices.groups.bs); // [4, 6]匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题它指向引用内容的开始和结束位置const reQuoted /“([^”])”/dgu;
function pointToQuotedText(str) {const startIndices new Set();const endIndices new Set();for (const match of str.matchAll(reQuoted)) {const [start, end] match.indices[1];startIndices.add(start);endIndices.add(end);}let result ;for (let index0; index str.length; index) {if (startIndices.has(index)) {result [;} else if (endIndices.has(index1)) {result ];} else {result ;}}return result;
}console.log(pointToQuotedText(They said “hello” and “goodbye”.));
// [ ] [ ] ················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇在知乎、掘金收获超百万阅读。从2014年起每年都会写一篇年度总结已经写了7篇点击查看年度总结。同时最近组织了源码共读活动帮助3000前端人学会看源码。公众号愿景帮助5年内前端人走向前列。识别上方二维码加我微信、拉你进源码共读群今日话题略。分享、收藏、点赞、在看我的文章就是对我最大的支持