1. ES6 - 对象字面量增强
对象字面量的增强写法主要体现在以下几个方面,以提高代码的可读性、可维护性和功能性:
- 属性简写:当属性名和变量名相同时,可以省略属性名,直接写变量名
- 方法简写:可以省略 function 关键字,直接写方法名
- 计算属性名:使用表达式的结果作为对象属性的键名
当前有多个变量,想要把它们放入到对象中,并且在对象中定义一个方法,在 ES6 之前是这样写的:
JavaScript 从 ES6 到 ES13 经历了显著的语言特性演进。详细解析了各版本核心更新,包括 ES6 的 let/const、箭头函数、模板字符串及数据结构(Set/Map);ES7 的指数运算符与 includes;ES8 的对象方法与字符串填充;ES10 的数组扁平化与参数处理;ES11 的空值合并、可选链及 globalThis;以及 ES12 和 ES13 引入的弱引用、逻辑赋值、负索引访问和类私有成员等。内容涵盖语法变化、作用域机制、内存管理及新 API 应用,旨在帮助开发者掌握现代 JavaScript 标准规范。
对象字面量的增强写法主要体现在以下几个方面,以提高代码的可读性、可维护性和功能性:
当前有多个变量,想要把它们放入到对象中,并且在对象中定义一个方法,在 ES6 之前是这样写的:
const name = 'liang'
const age = 20
const object = {
name: name,
age: age,
foo: function() {}
}
属性简写和方法简写的使用示例:
const name = 'liang'
const age = 20
const object = {
// property shorthand (属性简写)
name, age,
// method shorthand (方法简写)
foo() {}
}
可以使用箭头函数作为方法,这样可以更简洁地定义函数,但是要注意箭头函数中的 this 指向。
const object = {
foo() { console.log(this) },
bar: () => { console.log(this) }
}
计算属性名:[] 是计算属性名的语法,使用表达式的结果作为对象属性的键名。
const name = 'liang'
const object = {
// ES6 计算属性名,等价于 liang456
[name + 456]() { console.log('ES6 的计算属性写法'); }
}
object[name + '123'] = function() { console.log('ES6 之前的计算属性写法'); }
object.liang123()
object.liang456()
ES6 解构赋值是一种强大的语法特性,它允许你从数组或对象中提取数据,并将其赋值给变量。这种语法使得从复杂的数据结构中提取数据变得更加简洁和直观。
// 现有一个数组或对象,要将里面的多个值取出来
const arr = ['html', 'css', 'js']
// 在 ES6 之前,是这样获取的
const item1 = arr[0]
const item2 = arr[1]
const item3 = arr[2]
// ES6 新增解构赋值,可以这样获取
const [value1, value2, value3] = arr
// 对象的解构赋值
const { name, age } = { name: 'liang', age: 20 }
在 ES5 中声明变量都是使用的 var 关键字,从 ES6 开始新增了两个关键字可以声明变量:let、const。
// variable 变量
var a = 1
let b = 2
// constant 常量
const c = 3
通过 let、const 声明的变量不可以重复定义。
var a = 1
var a = 1
let b = 2
// let b = 2 // Uncaught SyntaxError: Identifier 'b' has already been declared
const c = 3
// const c = 3 // Uncaught SyntaxError: Identifier 'c' has already been declared
const 本质上是传递的值不可以修改,如果传递的是引用类型,可以通过引用找到对象,去修改对象内部的属性。
// const 本质上是传递的值不可以修改
// 但是,如果传递的是一个引用类型 (内存地址),可以通过引用找到对应的对象,去修改对象内部的属性
const obj = { name: 'liang' }
obj.name = 'hello'
let、const 和 var 的另一个重要区别是作用域提升:
console.log(a) // undefined
var a = 1
console.log(b) // Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 1
暂时性死区:
Window 对象添加属性:
var name = 'liang'
console.log(window.name) // liang
let title = 'vue'
console.log(window.title) // undefined
在 ES5 中只有两个东西会形成作用域:全局作用域、函数作用域。
在 ES6 中新增了块级作用域,并且通过 let、const、function、class 声明的标识符是具备块级作用域限制的。
if、switch、for 语句都是块级作用域。
// if 语句的代码就是块级作用域
if (true) {
let foo = 'foo'
}
console.log(foo) // Uncaught ReferenceError: foo is not defined
// switch 语句的代码也是块级作用域
let color = 'red'
switch (color) {
case 'red':
var bar = 'bar'
let bgColor = '#000'
}
console.log(bar)
console.log(bgColor) // Uncaught ReferenceError: bgColor is not defined
// for 语句的代码也是块级作用域(这个场景用的最多,强烈推荐使用 let)
for (var i = 0; i < 5; i++) {}
console.log(i) // 5
for (let j = 0; j < 5; j++) {}
console.log(j) // Uncaught ReferenceError: j is not defined
我们来看一下 for 循环语句中,起始变量使用 var 声明的导致的问题:无论点击哪个按钮输出结果都是一样的。
const btns = document.getElementsByTagName('button')
// 特别注意:这里是 var 声明的起始变量
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log('第' + i + '个按钮被点击') // 第 3 个按钮被点击
}
}
这是因为 var 声明的变量 i 是全局作用域,函数中没有变量 i 就会往上级作用域找,循环结束后 i 就已经变为 3。
学习过块级作用域后,只需要将 i 的声明从 var 改为 let 即可,这就是块级作用域给我们带来的好处。
// 使用 let 声明起始变量 i,使其形成块级作用域
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log('第' + i + '个按钮被点击')
}
}
ES6 模板字符串是一种增强型的字符串表示方式,使用反引号包裹内容,支持在字符串中嵌入变量、表达式以及多行文本,极大的简化了字符串拼接和格式化操作。
基本语法:模板字符串通过反引号定义,可以包含变量和表达式,使用 ${} 语法进行插入。
const string = `my name is ${name}, age is ${age + 5}, height is ${height}`
多行文本:模板字符串天然支持多行文本,无需使用 和字符串拼接,可以直接换行书写。
const html = `<div>
<button>按钮</button>
<button>按钮</button>
<button>按钮</button>
</div>`
标签模板字符串是 ES6 中的一个高级特性,它允许你通过一个函数来处理模板字符串。
// 当使用标签模板字符串调用函数时
// 第一个参数:依然是模版字符串中的整个字符串,只是被切成了多块,放到了一个数组中
// 第二个参数:模板字符串中的第一个 ${}
// 第三个参数:模板字符串中的第二个 ${}
function foo(m, n, x) {
console.log({ m, n, x })
}
// 标签模板字符串调用
const name = 'liang'
const age = 18
foo`hello${name}world${age}!`
在 ES6 中,函数参数的默认值功能极大的简化了函数定义和调用过程。
// ES6 可以给函数的参数设置默认值
function foo(m = '默认值') {
// ...
}
// 对象参数和默认值以及解构
function foo({ name, age, gender = '男' } = { name: 'liang', age: 20 }) {
// ...
}
函数的 length 属性值为其参数数量,对于有参数默认值的函数,从有默认值的参数开始,后续参数不做数量统计。
// 对于有参数默认值的函数,从有默认值的参数开始,后续参数不做数量统计
function bar(a, b, c = 3) {}
console.log(bar.length) // 2
ES6 中的剩余参数(Rest Parameter)是一种强大特性,可以将不定数量的参数放入到一个数组。
// 如果最后一个参数是以 ... 为前缀的,那么它会将剩余参数放到该参数中,并且作为一个数组
function foo(a, b, ...args) {
console.log(a) // 1
console.log(b) // 2
console.log(args) // [3, 4, 5]
}
foo(1, 2, 3, 4, 5)
箭头函数是没有显式原型的,所以不能作为构造函数,无法通过 new 来创建对象。
const foo = () => {}
console.log(foo.__proto__) // undefined
new foo() // Uncaught TypeError: foo is not a constructor
展开语法(Spread Syntax)最初在 ES6 中引入,主要用于数组操作,到了 ES9,展开语法被扩展到了对象处理。
// 1. 在函数调用时使用展开语法
function foo(a, b, c) {
console.log({ a, b, c })
}
foo(...['html', 'css', 'js'])
// 2. 构造数组时
const name = 'liang'
const hobby = ['html', 'css']
const newArr = [...name, ...hobby]
console.log(newArr)
// 3. 构建对象字面量 ES9(ES2018)
const info = { name: 'liang', age: 20 }
const object = { title: 'profile', ...info, gender: 1 }
console.log(object)
展开运算符进行的是一个浅拷贝。
const info = { name: 'liang', profile: { age: 18 } }
const user = { ...info }
user.profile.age = 20
console.log(info.profile.age) // 20
const num1 = 100 // 十进制
const num2 = 0b100 // 二进制
const num3 = 0o100 // 八进制
const num4 = 0x100 // 十六进制
console.log(num1) // 100
console.log(num2) // 4
console.log(num3) // 64
console.log(num4) // 256
// ES12(ES2011)对于大的数组,可以使用连接符 _
const num = 10_000_000_000
console.log(num) // 10000000000
Symbol 是 ES6 中新增的一个基本数据类型,翻译为:符号。
Symbol 即使多次创建值,它们也是不同的:Symbol 函数执行后,每次创建出来的值都是独一无二的。
// Symbol() 可以生成一个独一无二的值,可以让它作为对象的 key
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2) // false
// 创建 Symbol 值的时候传入一个描述
const s3 = Symbol('aaa')
console.log(s3.description) // aaa
Symbol 值作为对象的属性 key 的多种写法:
const s1 = Symbol()
const s2 = Symbol()
const s3 = Symbol()
const s4 = Symbol()
// 1. 在定义对象字面量时使用
const obj = {
[s1]: '111',
[s2]: '222'
}
// 2. 新增属性
obj[s3] = '333'
// 3. Object.defineProperty 方式
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: '444'
})
console.log(obj[s1]) // 111
前面我们说过,Symbol() 返回的值都是独一无二的,那么有没有办法让它返回相同的值呢?
Symbol.for(key) 是 ES6 中的一个静态方法,用于创建或查找全局注册表中的 Symbol。
// Symbol.for(key)
const s1 = Symbol.for('aaa')
const s2 = Symbol.for('aaa')
console.log(s1 === s2) // true
// Symbol.keyFor() 用于获取全局注册表中 Symbol 对应的键名
const key = Symbol.keyFor(s1)
console.log(key) // aaa
Set 是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是 Set 的元素不能重复。
// 通过构造函数创建 Set 结构
const set = new Set()
set.add(10)
set.add(20)
set.add(30)
set.add(30) // 不会被重复添加
console.log(set);
因为 Set 结构具有元素不会重复的特性,所以我们可以用于 数组去重。
// 利用 set 的特性去重
const arrSet = new Set([32, 10, 25, 10, 32, 15])
const newArr3 = [...arrSet]
Set 的常见属性和方法:
const set = new Set([32, 10, 25, 10, 32, 15])
console.log(set.size) // 4
set.add(18)
set.delete(10)
console.log(set.has(25)) // true
console.log(set) // Set(4) { 32, 25, 15, 18 }
WeakSet 中只能存放对象类型,不能存放基本数据类型。WeakSet 对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么 GC 可以对该对象进行回收。
const weakSet = new WeakSet()
let obj = { name: 'liang' }
weakSet.add(obj)
Map 是一种新的数据结构,用于存储键值对,它与传统对象(Object)相比,具有更灵活的键类型和更好的性能特性。
const map = new Map()
map.set(user, 'aaa')
map.set(profile, 'bbb')
map.set('name', 'liang')
console.log(map)
Map 常见属性和方法:
const map = new Map([['name', 'liang'], ['music', '天空之外']])
console.log(map.size) // 2
map.set('age', 20).set('height', 190)
console.log(map.get('name')) // liang
console.log(map.has('age')) // true
map.delete('height')
map.clear()
console.log(map)
WeakMap 的 key 只能使用对象,不接受其他的类型作为 key。WeakMap 的 key 对对象的引用是弱引用,如果没有其他引用引用这个对象,那么 GC 可以回收该对象。
let obj2 = { name: 'liang' }
const map2 = new WeakMap()
map2.set(obj2, 'bbb')
obj2 = null
ES7 新增了 includes 方法,可以直接判断数据是否在数组中。
const arr = ['html', 'css', 'js']
if (arr.includes('css')) {
console.log('在数组中')
}
ES7 新增的指数运算符:
// ES7 增加了一个运算符 **
console.log(2 ** 5)
在 ES8 中提供了 Object.values() 来获取所有的 value 值。
const user = { name: 'liang', age: 20, height: 180 }
console.log(Object.keys(user)) // ['name', 'age', 'height']
console.log(Object.values(user)) // ['liang', 20, 180]
通过 Object.entries() 可以获取到一个数组,数组中会存放可以枚举属性的键值对数组。
console.log(Object.entries(user))
在 ES8 中,引入了两个字符串方法:padStart、padEnd,用于在字符串的开头或结尾填充指定字符,使其达到指定长度。
const string = 'hello'
console.log(string.padStart(8))
console.log(string.padStart(8, '*'))
console.log(string.padEnd(8, '*'))
尾逗号(Trailing Commas)是指在数组、对象字面量或函数参数列表的最后一个元素后添加的额外逗号。
// ES8 才开始支持函数接收参数时或调用时最后面还有逗号
function foo(m, n,) {
console.log({ m, n })
}
foo(111, 222,)
flat() 和 flatMap() 是两个用于处理数组扁平化的常用方法。
const nums = [1, [2], [[3]], [[[4]]]]
console.log(nums.flat(1))
console.log(nums.flat(2))
console.log(nums.flat(Infinity))
flatMap() 方法的使用方法:
const slogan = ['what are you doing', 'how old are you']
const words2 = slogan.flatMap(item => item.split(' '))
console.log(words2)
Object.fromEntries() 方法用于将键值对列表转换为一个普通对象。
const entries = Object.entries(user)
console.log(Object.fromEntries(entries))
清除字符串中的空格:
const string = ' hello world '
console.log({ trimStart: string.trimStart() })
console.log({ trimEnd: string.trimEnd() })
在早期的 JavaScript 中,我们不能正确的表示过大的数字。大于 Number.MAX_SAFE_INTEGER 的数值,表示的值可能是不正确的。
// ES11 之后:Bigint(后面加一个 n)
const bigInt = 900719925474099100n
console.log(bigInt)
console.log(bigInt + 10n)
空值合并运算符(Nullish Coalescing Operator),当左侧操作数为 null/undefined 时,返回右侧操作数。
let bar = 0
console.log(bar ?? 'hello') // 0
ES11 进入了一个非常实用的特性:可选链操作符(Optional Chaining Operator)。
const profile = { name: 'liang' }
console.log(profile.info?.age)
globalThis 是 ES11 引入的一个标准化的全局对象引用,用于在任何 JavaScript 环境中统一访问全局对象。
// 浏览器环境
console.log(window)
// node 环境
console.log(global)
// ES11(在任何环境中都可以访问全局对象)
console.log(globalThis)
FinalizationRegistry 是 ES12 引入的一个高级特性,用于在对象被垃圾回收(GC)后执行清理回调。
let user = { name: 'liang' }
const finalRegistry = new FinalizationRegistry(() => {
console.log('注册到 finalRegistry 的某个对象被销毁');
})
finalRegistry.register(user)
user = null
WeakRef 是 ES12 引入的一个特性,用于创建对对象的弱引用。
let user = { name: 'liang' }
let info = new WeakRef(user)
console.log(info.deref())
JavaScript 中的三种逻辑赋值运算符是 ES12 引入的语法糖。
// 逻辑或赋值运算 ||=
let message = ''
message ||= 'default'
// 逻辑与赋值运算 &&=
let a = 1
let b = 2
a &&= b
// 空值合并赋值 ??=
let c = ''
let d = 2
c ??= d
String.prototype.replaceAll() 是 ES12 引入的一个字符串方法。
const string = 'good idea good'
console.log(string.replaceAll('good', 'aaa'))
console.log(string.replaceAll(/good/g, 'bbb'))
Array.prototype.at() 和 String.prototype.at() 是 ES13 引入的新方法,支持负数索引取值。
const arr = ['html', 'css', 'js', 'vue']
console.log(arr.at(-1)) // vue(最后一个)
ES13 中 Object 新增了一个静态方法(类方法):Object.hasOwn(obj, key)。
const user = { name: 'liang' }
console.log(Object.hasOwn(user, 'name')) // true
console.log(Object.hasOwn(user, 'age')) // false
从 ES13 开始,正式支持在类中定义真正的私有化属性(private)。
class Person {
height = 180
#info = 'real private info'
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.#info)
this.#showName()
}
#showName() {
console.log('name: ' + this.name);
}
}
const p = new Person('liang', 20)
p.running()
类的静态属性、静态方法使用示例:
class Person {
static name = 'liang'
static #age = 20
static showName() {
console.log(this.#age);
}
static {
console.log('hello world');
}
}
console.log(Person.name)
Person.showName()

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online