5 个一定要学会的 JavaScript 新特性
JavaScript新特性概览 本文介绍了JavaScript近年来的重要更新,包括: 更安全的属性访问方式 - Object.hasOwn替代in操作符和hasOwnProperty 真正的私有属性 - 使用#符号声明类私有字段 代码可读性增强 - 数字分隔符(1_000_000)和可选链操作符(?.) 大数计算支持 - BigInt类型 异步编程改进 - async/await和Promise
前言
JavaScript在不断地升级迭代,越来越多的新特性让我们的代码写起来变得简洁有趣,这篇文章会介绍5个新特性,一起研究一下把。
1.# 使用"Object.hasOwn"替代“in”操作符
有时,我们想知道对象上是否存在某个属性,一般会使用“in”操作符或“obj.hasOwnProperty”,但它们都有各自的缺陷。
in
如果指定的属性位于对象或其原型链中,“in”运算符将返回true。
const Person = function (age) {
this.age = age
}
Person.prototype.name = 'fatfish'
const p1 = new Person(24)
console.log('age' in p1) // true
console.log('name' in p1) // true 注意这里
obj.hasOwnProperty
hasOwnProperty 方法会返回一个布尔值,表示对象自身属性中是否具有对应的值(原型链上的属性不会读取)。
const Person = function (age) {
this.age = age
}
Person.prototype.name = 'fatfish'
const p1 = new Person(24)
console.log(p1.hasOwnProperty('age')) // true
console.log(p1.hasOwnProperty('name')) // fasle 注意这里
obj.hasOwnProperty已经可以过滤掉原型链上的属性,但在某些情况下,它还是不安全。
Object.create(null).hasOwnProperty('name')
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function
Object.hasOwn
别急,我们可以使用Object.hasOwn来避免这两个问题,这比“obj.hasOwnProperty”方法更加方便、安全。
let object = { age: 24 }
Object.hasOwn(object, 'age') // true
let object2 = Object.create({ age: 24 })
Object.hasOwn(object2, 'age') // false
let object3 = Object.create(null)
Object.hasOwn(object3, 'age') // false
2.# 使用"#"声明私有属性
以前,我们一般用_表示私有属性,但它并不靠谱,还是会被外部修改。
class Person {
constructor (name) {
this._money = 1
this.name = name
}
get money () {
return this._money
}
set money (money) {
this._money = money
}
showMoney () {
console.log(this._money)
}
}
const p1 = new Person('fatfish')
console.log(p1.money) // 1
console.log(p1._money) // 1
p1._money = 2 // 依旧可以从外部修改_money属性,所以这种做法并不安全
console.log(p1.money) // 2
console.log(p1._money) // 2
使用“#”实现真正私有属性
class Person {
#money=1
constructor (name) {
this.name = name
}
get money () {
return this.#money
}
set money (money) {
this.#money = money
}
showMoney () {
console.log(this.#money)
}
}
const p1 = new Person('fatfish')
console.log(p1.money) // 1
// p1.#money = 2 // 没法从外部直接修改
p1.money = 2
console.log(p1.money) // 2
console.log(p1.#money) // Uncaught SyntaxError: Private field '#money' must be declared in an enclosing class
3.# 超有用的"数字分隔符"
直接看例子,惊呆了我...
const sixBillion = 6000000000
// ❌ 难以阅读
const sixBillion2 = 6000_000_000
// ✅ 更加易于阅读
console.log(sixBillion2) // 6000000000
当然也可以使用"_"用于计算
const sum = 1000 + 6000_000_000 // 6000001000
4.# 使用 ?. 简化 && 和 三元运算符
这些例子,你一定非常熟悉,咱们有办法可以简化它吗?
const obj = null
console.log(obj && obj.name)
const $title = document.querySelector('.title')
const title = $title ? title.innerText : undefined
“?.”
const obj = null
console.log(obj?.name)
const $title = document.querySelector('.title')
const title = $title?.innerText
Tips
?. 的一般用法
-
obj?.prop 对象属性
-
obj?.[expr] 对象属性
-
func?.(...args) 执行函数
5.# 使用"BigInt"支持大数计算
JS中超过“Number.MAX_SAFE_INTEGER”的数字计算将是不安全的。
Example:
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// Math.pow(2, 53) => 9007199254740992
// Math.pow(2, 53) + 1 => 9007199254740992
使用"BigInt"完全可以避免这个问题
BigInt(Math.pow(2, 53)) === BigInt(Math.pow(2, 53)) + BigInt(1) // false
——————————————————————————————————————————
一、引言:JavaScript 的进化之路
JavaScript 自1995年诞生以来,经历了从简单的脚本语言到全栈开发语言的蜕变。自 ES6(ES2015)开始,TC39 委员会采用了每年发布新标准的模式,使 JavaScript 语言特性得到快速迭代和增强。本文将全面解析近年来 JavaScript 的重要新特性,帮助你掌握现代 JavaScript 开发的核心技能。
二、ES2015(ES6):革命性更新
2.1 let 和 const:块级作用域声明
javascript
// var 的问题:函数作用域,存在变量提升
function varProblem() {
console.log(x); // undefined(变量提升)
var x = 10;
if (true) {
var x = 20; // 覆盖外部变量
console.log(x); // 20
}
console.log(x); // 20
}
// let:块级作用域,无变量提升
function letSolution() {
let x = 10;
if (true) {
let x = 20; // 独立的作用域
console.log(x); // 20
}
console.log(x); // 10
// 暂时性死区(TDZ)
// console.log(y); // ReferenceError
let y = 30;
}
// const:声明常量
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
// const 对于对象的保护是浅层的
const obj = { a: 1 };
obj.a = 2; // 允许
// obj = { b: 2 }; // TypeError
2.2 箭头函数:简洁的语法和词法 this
javascript
// 传统函数
const add = function(a, b) {
return a + b;
};
// 箭头函数
const addArrow = (a, b) => a + b;
const square = x => x * x;
const log = () => console.log('Hello');
// this 绑定行为
const obj = {
value: 42,
traditional: function() {
console.log(this.value); // 42
},
arrow: () => {
console.log(this.value); // undefined(继承外层作用域)
}
};
// 事件处理中的优势
class Button {
constructor() {
this.count = 0;
this.element = document.createElement('button');
// 传统方式需要绑定 this
this.element.addEventListener('click', function() {
// this 指向 DOM 元素
this.count++; // 错误!
});
// 箭头函数自动绑定 this
this.element.addEventListener('click', () => {
this.count++; // 正确
});
}
}
2.3 模板字符串:强大的字符串处理
javascript
const name = 'Alice';
const age = 30;
// 基本用法
const greeting = `Hello, ${name}! You are ${age} years old.`;
// 多行字符串
const multiline = `
This is a
multiline
string.
`;
// 表达式计算
const calculation = `${2 + 3} = 5`;
// 嵌套模板
const isMember = true;
const message = `Welcome ${isMember ? 'back' : ''} ${name}!`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + str + value;
}, '');
}
const highlighted = highlight`Hello ${name}, you are ${age} years old.`;
2.4 解构赋值:优雅的数据提取
javascript
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [3, 4, 5]
// 交换变量
let a = 1, b = 2;
[a, b] = [b, a];
// 对象解构
const user = {
name: 'Alice',
age: 30,
address: { city: 'Beijing' }
};
const { name, age, address: { city } } = user;
// 函数参数解构
function printUser({ name, age = 18 }) {
console.log(`${name} is ${age} years old`);
}
printUser({ name: 'Bob' });
// 默认值
const { email = 'default@example.com' } = user;
// 重命名
const { name: userName } = user;
2.5 默认参数、Rest 和 Spread
javascript
// 默认参数
function greet(name = 'Guest', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
// Rest 参数(剩余参数)
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// Spread 运算符
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// 对象展开
const defaults = { color: 'red', size: 'medium' };
const config = { ...defaults, size: 'large' };
// 函数调用展开
Math.max(...[1, 2, 3, 4]); // 4
2.6 增强的对象字面量
javascript
// 属性简写
const x = 10, y = 20;
const point = { x, y };
// 方法简写
const calculator = {
add(a, b) {
return a + b;
},
multiply(a, b) {
return a * b;
}
};
// 计算属性名
const prop = 'name';
const obj = {
[prop]: 'Alice',
['age' + 'Value']: 30
};
// __proto__ 设置原型
const protoObj = { base: 'proto' };
const child = {
__proto__: protoObj,
childProp: 'child'
};
2.7 类:面向对象语法糖
javascript
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
return `Hello, I'm ${this.name}`;
}
// 静态方法
static compare(person1, person2) {
return person1.age - person2.age;
}
// Getter/Setter
get description() {
return `${this.name} (${this.age})`;
}
set birthday(years) {
this.age += years;
}
}
// 继承
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
// 方法重写
greet() {
return `${super.greet()} and I'm in grade ${this.grade}`;
}
}
// 使用
const student = new Student('Bob', 20, 'A');
console.log(student.greet());
2.8 模块系统
javascript
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default function multiply(a, b) {
return a * b;
}
// main.js
import multiply, { PI, add } from './math.js';
import * as MathUtils from './math.js';
// 动态导入
async function loadModule() {
const module = await import('./math.js');
console.log(module.add(2, 3));
}
2.9 Promise:异步编程的基石
javascript
// 创建 Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('Success!') : reject('Failed!');
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log(result);
return result + ' Again';
})
.then(processed => {
console.log(processed);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('Done');
});
// Promise 静态方法
Promise.all([promise1, promise2]) // 所有成功
.then(values => console.log(values));
Promise.race([promise1, promise2]) // 第一个完成
.then(value => console.log(value));
2.10 迭代器和生成器
javascript
// 迭代器协议
const iterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
return step <= 3 ?
{ value: step, done: false } :
{ done: true };
}
};
}
};
// for...of 循环
for (const value of iterable) {
console.log(value);
}
// 生成器函数
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// 生成器与迭代器
for (const num of numberGenerator()) {
console.log(num);
}
三、ES2016-ES2017:渐进增强
3.1 指数运算符和数组方法
javascript
// 指数运算符 const squared = 2 ** 3; // 8 const result = 2 ** 3 ** 2; // 512(从右向左结合) // Array.prototype.includes const arr = [1, 2, 3, NaN]; console.log(arr.includes(2)); // true console.log(arr.includes(NaN)); // true(indexOf 无法检测 NaN)
3.2 async/await:异步编程的革命
javascript
// 传统 Promise 链
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => console.error(error));
}
// async/await 版本
async function fetchDataAsync() {
try {
const response = await fetch('/api/data');
const data = await response.json();
const result = await processData(data);
return result;
} catch (error) {
console.error(error);
throw error;
}
}
// 并行执行
async function fetchMultiple() {
const [user, posts] = await Promise.all([
fetch('/api/user'),
fetch('/api/posts')
]);
// 顺序执行
for (const url of ['/api/1', '/api/2', '/api/3']) {
const data = await fetch(url);
console.log(data);
}
}
3.3 Object 和 String 的新方法
javascript
// Object.values / Object.entries
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
// Object.getOwnPropertyDescriptors
const descriptors = Object.getOwnPropertyDescriptors(obj);
// String padding
console.log('5'.padStart(2, '0')); // '05'
console.log('Hello'.padEnd(10, '!')); // 'Hello!!!!!'
// 函数参数列表支持尾逗号
function foo(
param1,
param2, // 尾逗号允许
) {
// ...
}
四、ES2018:异步迭代和对象扩展
4.1 异步迭代器
javascript
// 异步可迭代对象
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i < 3) {
await new Promise(resolve => setTimeout(resolve, 100));
return { value: i++, done: false };
}
return { done: true };
}
};
}
};
// for await...of
(async () => {
for await (const value of asyncIterable) {
console.log(value);
}
})();
4.2 对象 Rest/Spread
javascript
// 对象展开
const defaults = { color: 'red', size: 'medium' };
const userSettings = { size: 'large', theme: 'dark' };
const combined = { ...defaults, ...userSettings };
// 对象剩余参数
const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
console.log(rest); // { c: 3, d: 4 }
// 浅拷贝
const original = { x: 1, y: { z: 2 } };
const copy = { ...original };
4.3 Promise.prototype.finally
javascript
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => {
console.log('请求完成,无论成功失败');
// 清理工作
});
4.4 正则表达式增强
javascript
// 命名捕获组
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = dateRegex.exec('2024-01-15');
console.log(match.groups.year); // '2024'
// 反向断言
const priceRegex = /\d+(?=美元)/; // 正向先行断言
const priceRegex2 = /(?<=价格:)\d+/; // 正向后行断言
// Unicode 属性转义
const emojiRegex = /\p{Emoji}/u; // 匹配表情符号
五、ES2019:语法完善
5.1 Array.prototype.flat 和 flatMap
javascript
// flat
const nested = [1, [2, [3, [4]]]];
console.log(nested.flat()); // [1, 2, [3, [4]]]
console.log(nested.flat(2)); // [1, 2, 3, [4]]
console.log(nested.flat(Infinity)); // [1, 2, 3, 4]
// flatMap
const sentences = ['Hello world', 'Good morning'];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ['Hello', 'world', 'Good', 'morning']
5.2 Object.fromEntries
javascript
// 键值对数组转对象
const entries = [['name', 'Alice'], ['age', 30]];
const obj = Object.fromEntries(entries);
// { name: 'Alice', age: 30 }
// Map 转对象
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
const mapObj = Object.fromEntries(map);
// 对象转换操作
const original = { a: 1, b: 2, c: 3 };
const transformed = Object.fromEntries(
Object.entries(original).map(([key, value]) => [key, value * 2])
);
5.3 String 和 try-catch 改进
javascript
// trimStart 和 trimEnd
const str = ' Hello ';
console.log(str.trimStart()); // 'Hello '
console.log(str.trimEnd()); // ' Hello'
// 可选 catch 绑定
try {
JSON.parse(invalidJson);
} catch { // 不需要 error 参数
console.log('解析失败');
}
// Symbol.prototype.description
const sym = Symbol('mySymbol');
console.log(sym.description); // 'mySymbol'
六、ES2020:现代化特性
6.1 可选链操作符(?.)
javascript
// 安全的属性访问
const user = { profile: { name: 'Alice' } };
console.log(user?.profile?.name); // 'Alice'
console.log(user?.settings?.theme); // undefined
// 方法调用
const obj = { method: () => 'hello' };
console.log(obj.method?.()); // 'hello'
console.log(obj.nonexistent?.()); // undefined
// 数组访问
const arr = [1, 2, 3];
console.log(arr?.[0]); // 1
console.log(arr?.[10]); // undefined
// 与 nullish 合并运算符结合
const theme = user?.settings?.theme ?? 'default';
6.2 空值合并运算符(??)
javascript
// 只对 null 或 undefined 有效
const value = null ?? 'default'; // 'default'
const value2 = 0 ?? 'default'; // 0(与 || 不同)
// 实际应用
const config = {
timeout: 0,
retries: null
};
const timeout = config.timeout ?? 1000; // 0
const retries = config.retries ?? 3; // 3
6.3 BigInt:大整数支持
javascript
// BigInt 字面量
const big = 1234567890123456789012345678901234567890n;
// 创建 BigInt
const bigFromNumber = BigInt(Number.MAX_SAFE_INTEGER);
const bigFromString = BigInt('9007199254740993');
// 运算
const sum = big + 1n;
const product = big * 2n;
// 比较
console.log(1n === 1); // false(类型不同)
console.log(1n == 1); // true
// 限制:不能与 Number 混合运算
// console.log(1n + 1); // TypeError
6.4 Promise.allSettled 和动态导入
javascript
// Promise.allSettled
const promises = [
Promise.resolve('success'),
Promise.reject('error'),
Promise.resolve('another success')
];
Promise.allSettled(promises).then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index}: ${result.value}`);
} else {
console.log(`Promise ${index}: ${result.reason}`);
}
});
});
// 动态导入
async function loadModule(condition) {
if (condition) {
const module = await import('./heavy-module.js');
module.heavyOperation();
}
}
// 按需加载组件
const loadComponent = (componentName) =>
import(`./components/${componentName}.js`);
6.5 globalThis 和 String.matchAll
javascript
// globalThis:跨环境全局对象 console.log(globalThis === window); // 浏览器环境 console.log(globalThis === global); // Node.js 环境 // String.prototype.matchAll const regex = /test(\d)/g; const str = 'test1test2test3'; const matches = [...str.matchAll(regex)]; // 每个匹配包含完整匹配和捕获组
七、ES2021:实用增强
7.1 逻辑赋值运算符
javascript
// 逻辑与赋值 (&&=) let x = 1; x &&= 2; // x = x && 2 console.log(x); // 2 let y = 0; y &&= 2; console.log(y); // 0 // 逻辑或赋值 (||=) let a = null; a ||= 'default'; // a = a || 'default' console.log(a); // 'default' let b = 'existing'; b ||= 'default'; console.log(b); // 'existing' // 空值合并赋值 (??=) let c = null; c ??= 'default'; // c = c ?? 'default' console.log(c); // 'default' let d = 0; d ??= 'default'; console.log(d); // 0
7.2 数字分隔符和 Promise.any
javascript
// 数字分隔符
const billion = 1_000_000_000;
const binary = 0b1010_0001_1000_0101;
const hex = 0xFF_FF_FF;
// Promise.any
const promises = [
Promise.reject('Error 1'),
Promise.resolve('Success 1'),
Promise.resolve('Success 2')
];
Promise.any(promises)
.then(first => console.log(first)) // 'Success 1'
.catch(errors => console.log(errors.errors));
// String.prototype.replaceAll
const text = 'Hello world world';
const newText = text.replaceAll('world', 'there');
// 'Hello there there'
八、ES2022:类增强和顶层 await
8.1 类的私有字段和方法
javascript
class Counter {
// 私有字段
#count = 0;
// 私有方法
#increment() {
this.#count++;
}
// 公共方法访问私有字段
tick() {
this.#increment();
return this.#count;
}
// 静态私有字段
static #maxInstances = 10;
// 私有字段检查
static #validateInstance() {
// ...
}
}
const counter = new Counter();
console.log(counter.tick()); // 1
// console.log(counter.#count); // SyntaxError
8.2 类的静态块和静态字段
javascript
class Database {
// 静态公共字段
static host = 'localhost';
static port = 5432;
// 静态私有字段
static #connection;
// 静态初始化块
static {
// 复杂的静态初始化逻辑
try {
this.#connection = this.#establishConnection();
} catch (error) {
console.error('连接失败:', error);
}
}
static #establishConnection() {
// 建立连接
return { connected: true };
}
}
8.3 顶层 await
javascript
// 模块顶层可以直接使用 await
const data = await fetch('/api/data').then(r => r.json());
console.log(data);
// 动态导入中使用
const module = await import('./module.js');
// 错误处理
try {
const result = await riskyOperation();
} catch (error) {
console.error('操作失败:', error);
}
8.4 其他新特性
javascript
// Array.prototype.at
const arr = [1, 2, 3, 4, 5];
console.log(arr.at(0)); // 1
console.log(arr.at(-1)); // 5(负数索引)
console.log(arr.at(-2)); // 4
// Object.hasOwn(替代 hasOwnProperty)
const obj = { a: 1 };
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
// 错误实例的 cause 属性
try {
throw new Error('外层错误', {
cause: new Error('原始错误')
});
} catch (error) {
console.log(error.message); // '外层错误'
console.log(error.cause); // Error: 原始错误
}
九、ES2023:数组增强
javascript
// findLast 和 findLastIndex
const numbers = [1, 2, 3, 4, 5, 2];
// 查找最后一个符合条件的元素
const lastEven = numbers.findLast(n => n % 2 === 0); // 2
const lastEvenIndex = numbers.findLastIndex(n => n % 2 === 0); // 5
// 实际应用:查找最近的日志
const logs = [
{ id: 1, message: 'Start', timestamp: '2024-01-01' },
{ id: 2, message: 'Error', timestamp: '2024-01-02' },
{ id: 3, message: 'End', timestamp: '2024-01-03' }
];
const lastError = logs.findLast(log => log.message.includes('Error'));
// 使用 Symbol 作为 WeakMap/WeakSet 的键
const weakMap = new WeakMap();
const key = Symbol('unique');
weakMap.set(key, 'value');
十、实验性特性前瞻
10.1 装饰器(Decorators)
javascript
// 类装饰器
@log
class MyClass {
@readonly
@enumerable(false)
method() {}
@debounce(300)
handleClick() {}
}
// 自定义装饰器实现
function log(constructor) {
return class extends constructor {
constructor(...args) {
console.log('实例化:', constructor.name);
super(...args);
}
};
}
10.2 管道运算符(Pipeline Operator)
javascript
// 提案中的管道运算符 const result = value |> double |> add(10) |> format; // 替代方案 const result = format(add(10)(double(value)));
十一、最佳实践和兼容性考虑
11.1 特性检测和渐进增强
javascript
// 检测新特性支持
const supportsOptionalChaining = () => {
try {
eval('const obj = {}; obj?.property');
return true;
} catch {
return false;
}
};
// 使用 polyfill
if (!Array.prototype.flat) {
Array.prototype.flat = function(depth = 1) {
// polyfill 实现
};
}
// Babel 和 TypeScript 配置
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: '> 0.5%, not dead',
useBuiltIns: 'usage',
corejs: 3
}]
]
};
11.2 性能考虑
javascript
// 可选链 vs 传统判断
// 可选链更简洁,性能相近
const value = obj?.prop?.subProp; // 推荐
// 展开运算 vs Object.assign
const merged = { ...obj1, ...obj2 }; // 性能更好
// 循环中的优化
// 避免在循环中创建箭头函数
// 使用 for...of 而不是 forEach 当需要 break 时
十二、总结
JavaScript 的持续演进使其保持了强大的生命力。从 ES6 的革命性更新到后续每年的渐进增强,现代 JavaScript 提供了:
-
更简洁的语法:箭头函数、解构、模板字符串
-
更强大的异步处理:Promise、async/await、异步迭代器
-
更好的面向对象支持:类、私有字段、静态块
-
更安全的代码:可选链、空值合并、const/let
-
更丰富的内置方法:数组、字符串、对象的扩展方法
作为开发者,我们应该:
-
持续学习新特性,但保持对兼容性的关注
-
在项目中合理使用新特性,提升代码质量和开发效率
-
理解特性背后的原理,而不只是语法糖的使用
-
关注 TC39 提案,了解 JavaScript 的发展方向
更多推荐
所有评论(0)