1. ES6
对象字面量增强写法
var name = 'coder'
var age = 18
// 当键名(key)和值名(value)一样时可以简写
var obj = {
name: name,
age: age,
foo: function() {
console.log(this)
},
bar: () => {
console.log(this)
}
}
var obj = {
// 1. property shorthand(属性简写)
name,
age,
// 2. method shorthadn(方法的简写)
foo() {
console.log(this)
}
// 3. computed property name(计算属性名)
[name + 123]: 'hahahah' // coder123: 'hahahah'
}
... 解构
数组的解构
var names = ['abc', 'cba', 'nba']
// var item1 = names[0]
// var item2 = names[1]
// var item3 = names[2]
// 对数组的解构: []
var [item1, item2, item3] = names
console.log(item1, item2, item3) // 'abc', 'cba', 'nba'
// 解构后面的元素
var [, , itemz] = names
console.log(itemz) // nba
// 解构出一个元素,后面的元素放到一个新数组中
var [itema, ...newNames] = names
console.log(itema) // abc
console.log(newNames) // ['cba', 'nba']
// 解构的默认值
var [itema, itemb, itemc, itemd = 'aaa']
console.log(itemd) // aaa
对象的解构
var obj = {
name: 'coder',
age: 18,
height: 1.88
}
// 对象的解构: {} 根据key赋值,不是按顺序赋值的
var { name, height ,age } = obj
console.log(name, age, height) // coder, 18, 1.88
var { age } = obj
console.log(age) // 18
// 解构出来后,重命名
var { name: newName } = obj
console.log(newName)
// 解构出来后,重命名,当没有这个值时,给一个默认值
var {addresss: newAddress = '长沙市'}
console.log(newAddress) // 长沙市
// 解构的在函数中的使用
function foo({ name, age }) {
console.log(name,age) // coder, 18
}
foo(obj)
let const
// es6之前声明变量的方式
var foo = 'foo'
// es6声明变量的方式
let bar = 'bar'
const name = 'abc' // const constant(常量、衡量)
// name = 'cba' // 报错
// 注意事项一:const本质上是传递的值不可以修改
// 但是如果传递的是一个引用类型(内存地址),可以通过引用找到对应的对象,去修改对象内部的属性,这个是可以的
const obj = {
foo: 'foo'
}
// obj = {} // 报错
obj.foo = 'aaa'
console.log(obj.foo) // aaa
// 注意事项二: 通过let const 定义的变量名是不可以重复定义的
// var foo = 'abc'
// var foo = 'cba'
let foo = 'abc'
let foo = 'cba' // SyntaxError: -Identifier: 'foo ' has already been declared
作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升
let,const会在执行上下文创建阶段被创建出来,但是不可以被访问,所以他们没有作用域提升
// console.log(foo) undefined
// var foo = 'foo'
// Reference(引用)Error; Cannot access 'foo ' before initialization(初始化)
// let/ const 他们是没有作用域提升
// foo被创建出来了,但是不能被访问
// 作用域提升: 能提前被访问
console.log(foo)
let foo = 'foo'
let-const 暂时性死区
var foo = 'foo'
if(true) {
console.log(foo) // 在这里是访问不到foo的,这种现象,称之为暂时性死区
let foo = 'coder'
}
let-const 和window的关系
var foo = 'foo'
var message = 'Hello World'
/** (早期的ECMA标准)每一个执行上下文会被关联到一个变量环境(variable object,v0),在源代码
中的变量和函数声明会被作为属性添加到VO中。对于函数来说,参数也会被添加到VO中。**/
console.log(window.foo) // foo
console.log(window.message) // Hello World
window.message = '哈哈哈'
console.log(message) // 哈哈哈
/** (最新的ECMA标准)每一个执行上下文会关联到一个变量环境(VariableEnvironment)中,在执行代码
中变量和函数的声明会作为环境记录(Environment Record)添加到变量环境中。**/
// 对于函数来说,参数也会被作为环境记录添加到变量环境中。
`1. 变量被保存到VariableMap中`
`2. window对象是早期的GO对象,在最新的实现中其实是浏览器添加的全局对象`
let foo = 'foo'
console.log(window.foo) // undefined
块级作用域
// ES5中没有块级作用域
{
var foo = 'foo'
}
console.log(foo) // foo
// 在ES5中只有两个东西可以形成作用域
/**
1. 全局作用域
2. 函数作用域
**/
function foo() {
var bar = 'bar'
}
console.log(bar) // bar is not defined
/**
ES6的块级作用域只对let/const/function/class声明的类型有效
**/
{
let foo = 'why'
function demo() {
console.log('demo function')
}
class Person {}
}
console.log(foo) // foo is not defined
// 不同的浏览器有不同实现的(大部分浏览器为了兼容以前的代码,让function是没有块级作用域)
demo() // demo function
var p = new Person() // Person is not defined
if - switch - for 块级代码
// if语句的代码就是块级作用域
if(true) {
var foo = 'foo'
let bar = 'bar'
}
console.log(foo) // foo
console.log(bar) // bar is not defined
// switch语句的代码就是块级作用域
var color = 'red'
switch(color) {
case 'red':
var foo = 'foo'
let bar = 'bar'
}
console.log(foo) // foo
console.log(bar) // bar is not defined
// for语句的代码就是块级作用域
for (var i = 0; i < 10; i++) {
}
console.log(i) // 10
for (let i = 0; i < 10; i++) {
}
console.log(i) // i is not defined
`块级作用域的应用场景`
// const btns = document.getElementsByTagName('button)
for(var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log('第'+ i + '个按钮点击了') // 在外面点击打印的永远是btns.length个
}
}
// es5时期的解决方案
for(var i = 0; i < btns.length; i++) {
(function(n) {
btns[i].onclick = function() {
console.log('第'+ i + '个按钮点击了')
}
})(i)
}
// ES6可以直接使用let产生块级作用域
for(let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log('第'+ i + '个按钮点击了')
}
}
for...of ES6新增的遍历数组(对象)
const names = ['abc', 'cba', 'nba']
/**
可以使用break,continue,return
支持数组和类数组对象的遍历,循环读取键值
也支持字符串的遍历
**/
for(const item of names ) {
console.log(item)
/**
abc
cba
nba
**/
}
for...in ES6新增的遍历对象
const obj = {
age: 18,
name: 'coder'
height: 1.88
}
/**
循环遍历对象自身和继承的可枚举属性(不含Symbol)
会循环原型链和手动添加的键,返回键名key
**/
for(let key in obj) {
console.log(obj[key])
}
`` 字符串模板的基本使用
// ES6之前拼接字符串和其他标识符
const name = 'coder'
const age = 18
const height = 1.88
// my name iscoder, age is18, heigth is 1.88
console.log('my name is ' + name + ', age is ' + age + ', heigth is ' + height)
// ES6提供模板字符串 ``
const message = `my name is ${name}, age is ${age}, heigth is ${height}`
console.log(message) // my name is coder, age is 18, heigth is 1.88
标签模板字符串
function foo(m, n, z) {
console.log(m, n, z)
}
foo('hello', 'World', '---------') // hello World ---------
// 另外一种调用函数的方式: 标签模板字符串
// foo``
const name = 'coder'
const age = 18
const height = 1.88
// 第一个参数依然是模板字符串中整个字符串,只是被切割成了多块,放到数组中
// 第二个参数是模板字符串中的第一个${},一次类推
foo`He${height}llo${name}Wo${age}rld` // ['He', 'llo', 'Wo', 'rld', raw: Array(4)] 1.88 'coder'
ES6中函数的默认参数
// ES5以及之前给参数默认值
/**
* 1. 写起来麻烦,并且代码的阅读性比较差
* 2. 这种写法有bug
**/
function foo(m, n) {
m = m || 'bbb'
n = n || 'aaa'
console.log(m, n)
}
foo(0, '')
// ES6可以给函数参数提供默认值
function foo(m='aaa', n='bbb') {
console.log(m,n)
}
// 对象参数的默认值以及解构
function printInfo({ name, age } = { name: 'coder', age: 18 }) {
console.log(name, age)
}
printInfo({name: 'kobe', age: 18})
// 另外一种写法(给一个空对象为默认值,但是解构不出来,又给了name默认值和age默认值)
fucntion printInfo1({name = 'coder', age = 19} = {}) {
console.log(name, age)
}
printInfo1()
// 有默认值的形参最好放到最后
function bar(x, y, z = 30) {
console.log(x, y, z)
}
bar(10,20)
函数的剩余参数
剩余参数喝arguments的区别
1. 剩余参数只包含那些没有对应形参的实参,而arguments对象包含了传给函数的所有实参;
2. arguments对象不是一个真正的数组,而rest参数是一个真正的数组,可以进行数组的所有操作;
3. arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供并且希望以此来替代arguments的;
function foo(m, n, ...args) {
console.log(m, n) // 10, 20
console.log(args) // [30, 40, 50]
}
foo(10, 20, 30, 40, 50)
箭头函数
const bar = () => {
console.log(this, arguments)
// 箭头函数不绑定this,this去上层作用域找
// 箭头函数没有arguments
}
console.log(bar.prototype) // undefined 箭头函数没有显式原型的
var f = new far() // TypeError: far is not a constructor
展开语法
展开运算符是浅拷贝
const names = ['abc', 'cba', 'nba']
const name = 'why'
// 1.函数调用时
function foo(x, y, z) {
console.log(x, y, z)
}
// foo.apply(null, names) ES5的调用方式
foo(...names) // abc cba nba
foo(...name) // w h y
// 2. 构造数组时
const newNames = [...names, ...name]
console.log(newNames) // ['abc', 'cba', 'nba', 'w', 'h', 'y']
// 3. 构建对象字面量时ES9
const obj = { ...names, address: '广东省'}
console.log(obj) // {0: 'abc', 1: 'cba', 2: 'nba', address: '广东省'}
// 4. 展开运算符做的是浅拷贝
const info = {
name: 'coder'
friend: { name: 'kobe' }
}
const obj = {...info, name: 'jeka'}
obj.friend.name = 'james'
console.log(info.friend.name) // james
表示数值的方式
const num1 = 100 // 十进制
console.log(num1) // 100
// b -> binary
const num2 = 0b100 // 二进制
console.log(num2) // 4
// o -> octonary
const num3 = 0o100 // 八进制
console.log(num3) // 64
// x -> hexadecimal
const num4 = 0x100 // 十六进制
console.log(num4) // 256
// 大的数值的连接符(ES12)
const num = 1_000_000_000_000
console.log(num) // 1000000000000
Symbol
Symbol是ES6中新增的一个基础数据类型,翻译为符合
特性: 创建一个独一无二的key
// ES6之前,对象的属性名(key)只能使用字符串
const obj = {
name: 'coder'
friend: { name: 'kode' },
age: 18
}
obj['newName'] = 'james'
console.log(obj.newName) // james
// ES6中Symbol的基本使用
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2) // false
// ES10中, Symbol还有一个描述(descripiton)
const s3 = Symbol('aaa')
console.log(s3.description) // aaa
// Symbol值作为key
const obj = {
[s1]: 'bac',
[s2]: 'cba'
}
obj[s3] = 'nba'
const s4 = Symbol()
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: 'mba'
})
console.log(obj[s1], obj[s2], obj[s3], obj[s4]) // bac cba nba mba
console.log(obj.s1) // undefined 不能通过.语法获取
// 使用Symbol作为key属性名,在遍历Object.keys等中是获取不到这些Symbol值的
// 需要 Object.getOwnPropertySymbols()获取所有Symbol的key
console.log(Object.keys(obj)) // []
console.log(Object.getOwnPropertyNames(obj)) // []
console.log(Object.getOwnPropertySymbols(obj))
// [Symbol(), Symbol(), Symbol(aaa), Symbol()]
const sKeys = Object.getOwnPropertySymbols(obj)
for(const sKey of skeys) {
console.log(obj[sKeys])
}
// Symbol.for(key) 创建一样的Symbol key
const sa = Symbol.for('aaa')
const sb = Symbol.for('aaa')
console.log(sa === sb) // true
const key = Symbol.keyFor(sa) // 返回Symbol的key
console.log(key) // aaa
const sc = Symbol.for(key)
console.log(sa === sc) // true
set的基本使用
ES6之前,我们存储数据的结构主要有两种: 数组,对象
ES6中新增了另外两种数据结构:Set, Map, 以及它们的另外形式weakSet, WeakMap
Set 是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复
// 创建Set结构
const set = new Set()
set.add(10)
set.add(20)
set.add(40)
set.add(4440)
set.add(10) // set数据不可以重复
// 这里是两个对象
// set.add({})
// set.add({})
// const obj = {}
// 这里是添加同一个对象,只会存在一个
// set.add(obj)
// set.add(obj)
console.log(set) // Set(4) {10, 20, 40, 4440}
// 2. 对数组去重(去除重复的元素)
const arr = [33, 22, 11, 10, 11]
// const newArr = []
// for(const item of arr) {
// if(newArr.indexOf(item) === -1) {
// newArr.push(item)
// }
// }
// console.log(newArr) // [33, 22, 11, 10]
const arrSet = new Set(arr)
// console.log(arrSet) // Set(4) {33, 22, 11, 10}
// const newArr = Array.from(arrSet)
// console.log(newArr) // [33, 22, 11, 10]
// const newArr = [...arrSet]
// console.log(newArr) // [33, 22, 11, 10]
// Set的属性和方法
`size属性`得到set里面数据的个数
console.log(arrSet.size)
`add`添加
arrSet.add(100)
`delete`删除
arrSet.delete(100)
`has`判断是否包含某个元素,返回true,false
arrSet.has(100) // false
`clear`清除Set,里面所有的元素都会被删掉
arrSet.clear()
`对Set进行遍历`
arrSet.forEach(item => {
console.log(item)
})
for (const item of arrSet) {
console.log(item)
}
WeakSet的使用
和Set类似的另外一个数据结构称为WeakSet, 也是内部元素不能重复的数据结构
WeakSet和Set的区别:
1. WeakSet中只能存放对象类型,不能存放基本数据类型
2. WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收
const weakSet = new WeakSet()
// 1. 只能存放对象类型
weakSet.add(10) // TypeError: Invalid value used in weak set
// 2. 对对象是一个弱引用
let obj = { name: 'coder' }
const set = new Set()
// 建立的是强引用
set.add(obj)
// 建立的是弱引用
weakSet.add(obj)
// WeakSet常见方法
`add 添加元素 返回WeakSet本身`
`delete 删除和这个值相等的元素,返回boolean类型`
`has 判断weakset中是否存在某个元素,返回boolean类型`
// 3. WeakSet的应用场景
const personSet = new WeakSet()
class Person {
constructor() {
personSet.add(this)
}
running() {
if(!personSet.has){
throw new Error('不能通过非构造方法创建出来的对象调用running方法')
}
console.log('running', this)
}
}
let p = new Person()
p.running()
p.running.call({name: 'coder'})
Map的基本使用
ES6新增的数据结构,用于存储映射关系
可以使用对象做为key,key也是唯一的
对对象的引用是强引用
// 1. JavaScript 中对象是不能使用对象来作为key的
const obj1 = { name: 'coder' }
const obj2 = { name: 'kebe' }
const info = {
[obj1]: 'aaa',
[obj2]: 'bbb'
}
console.log(info) // {[object Object]: 'bbb'}
// 2. Map就是允许我们对象类型作为key的
// 构造方法的使用
const map = new Map()
map.set(obj1, 'aaa')
map.set(obj2, 'bbb')
map.set(1, 'ccc')
console.log(map) // Map(3) {{…} => 'aaa', {…} => 'bbb', 1 => 'ccc'}
const map2 = new Map([[obj1, 'aaa'], [obj2, 'bbb'], [2. 'ccc']])
console.log(map2)
// 3. 常见的属性和方法
console.log(map2.size)
// set
map2.set('coder', 'eee')
// get
map2.get('coder')
// has 判断map里是否有某一个key,返回boolean
map2.has('coder')
// delete 删除某一个key,返回boolean
map2.delete('coder')
// clear 清除所有
map.clear('coder')
// 4. 遍历map
map2.forEach((item, key) => {
console.log(item, key)
})
for(const item of map2) {
console.log(item[0], item[1])
}
for(const [key, value] of map2) {
console.log(key, value)
}
WeakMap的使用
WeakMap和Map的区别
1. WeakMap只能使用对象,不能接受其他类型作为key
2. WeakMap的key对对象的引用是弱引用,如果没有其他引用使用这个对象,GC会回收这个对象
const obj1 = {name: 'obj1'}
const map = new Map()
map.set(obj1, 'aaa')
obj1 = null
'{name: 'obj1'} 这个对象是不会销毁的, 因为map使用了这个对象,是强引用'
const weakMap = new WeakMap()
weakMap.set(obj1, 1)
obj1 = null
// {name: 'obj1'} 会被GC回收
// 常见的方法
// get
console.log(weakMap.get(obj1))
// set
console.set(weakMap.get(obj1, '123'))
// has
console.has(weakMap.get(obj1))
// delete
console.has(weakMap.delete(obj1))
// 应用场景(vue3响应式原理中使用了WeakMap)
const obj = {
name: 'coder',
age: 18
}
function objNameFn1() {
console.log('obj1NameFn1被执行')
}
function objAgeFn1() {
console.log('obj1AgeFn1被执行')
}
// 创建WeakMap
const seakMap = new WeakMap()
// 对obj1收集的数据结构
const obj1Map = new Map()
obj1Map.set('name', [objNameFn1])
obj1Map.set('age', [objAgeFn1])
weakMap.set(obj1,obj1Map )
// 如果obj.name 发生了改变
// proxy/ Object.defineProperty
ES7
Array Includes
在ES7之前,判断一个数组中是否包含某个元素,需要通过indexOf获取结果,并且判断是否为-1
const names = ['abc', 'cba', 'nba', NaN]
if(names.indexOf('cba') !== -1) {
console.log('包含abc元素')
}
// ES7
// 第一个参数,传入要验证包含的元素, 第二个是索引,从那个位置开始验证, 返回boolean
if(names.includes('cba', 2)) {
console.log('包含abc元素')
}
// 区别
console.log(names.indexOf(NaN)) // -1
console.log(names.includes(NaN)) // true
指数(乘方)运算符
// 获取3的3次方
const result1 = Math.pow(3, 3)
// ES7
const result2 = 3 ** 3
console.log(result1, result2 ) // 27 27
ES8
Object values
ES5以前可以通过 Object.keys 获取一个对象所有的key
ES8中提供了, Object.values 来获取所有的value值
const obj = {
name: 'coder',
age: 19,
height: 1.88
}
console.log(Object.keys(obj)) // ['name', 'age', 'height']
console.log(Object.values(obj)) // ['coder', 19, 1.88]
console.log(Object.values(['abc', 'cba', 'nba'])) // ['abc', 'cba', 'nba']
console.log(Object.values('abc')) // ['a', 'b', 'c']
Object entries
通过Object.entries 可以获取到一个数组,数组中会存放可枚举属性的键值对数组
const obj = {
name: 'coder',
age: 19
}
console.log(Object.entries(obj)) // [Array(2), Array(2)]
const objEntries = Object.entries(obj)
objEntries.forEach(item => {
console.log(item[0], item[1])
})
console.log(Object.entries(['abc', 'cba', 'nba']))
// [['0', 'abc'],['1', 'cba'],['2', 'nba']]
String Padding
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8中增加了padStart和 padEnd方法,分别是对字符串的首尾进行填充的。
const message = 'Hello World'
// 第一个参数是填充后的长度, 第二个参数是填充的内容
const newMessage = message.padStart(15, '*').padEnd(20, '_')
// 案例
const cardNumber = '45642348678612354231'
const lastFourCard = cardNumber.slice(-4)
const finalCard = lastFourCard.padStart(cardNumber.length, '*')
console.log(finalCard) // ****************4231
Tariling Commas
function foo(m, n,) {
// ES8 可以在参数后面可以多加一个逗号,不会报错
}
foo(20, 30,)
Object Descriptors
获取一个对象的所有属性描述符
async await
async用于声明function异步, await 用于等待一个异步方法执行完成
async异步函数的写法
// 第一种普通函数
async function foo() {
}
// 第二种箭头函数
const foo = async() => {
}
// 第三章类中方法的异步
class bar {
async foo() {
}
}
异步函数的执行流程
async function foo() {
console.log(1);
console.log(2);
console.log(3);
}
console.log('script start');
foo()
console.log('script end');
/**
* 当里面没有其他代码时。跟平常函数执行是一样的
* script start
* 1
* 2
* 3
* script end
* **/
和普通函数的区别一 (返回值)
async function foo() {
console.log('foo function start');
console.log('中间代码');
console.log('foo function end');
// 第一种返回一个值
// return 'abc'
// 第二种返回一个thenable
// return {
// then: function(resolve) {
// resolve('hhh')
// }
// }
// 第三种返回一个promise
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('hahahah')
},1000)
})
}
// 异步函数的返回值一定是一个Promise
const promise = foo()
promise.then(res => {
// then接受的参数是异步函数return的值
console.log('promise then function exec', res);
})
和普通函数的区别二(异常)
async function foo() {
console.log('foo function start');
console.log('中间代码');
// 异步函数中的异常,会被作为异步函数返回的promise的reject值的
throw new Error('error message')
console.log('foo function end');
}
// 异步函数的返回值一定是一个Promise
const promise = foo()
promise.catch(err => {
console.log('err: ', err);
})
await关键字
1. await是后面会跟上一个表达式,这个表达式会返回一个Promise
2. await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数
如果await后面是一个普通的值,那么会直接返回这个值
如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
function requestData() {
return new Promise(resolve => {
setTimeout(() => {
resolve(2222)
},2000)
})
}
async function foo() {
const res = await requestData()
// 这里的代码就想当于是then方法里的代码
console.log('后面的代码1',res);
const res1 = await requestData()
console.log('后面的代码2',res1);
}
// 2.跟上其他的值
async function foo() {
const res1 = await 123
console.log(res1);
const res2 = await {
then: function(resolve) {
resolve('abc')
}
}
console.log(res2);
const res3 = await new Promise(resolve => {
resolve('coder')
})
console.log(res3);
}
// foo()
// 3. reject值
function requestData1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
reject(2222)
},2000)
})
}
async function foo1(){
const res = await requestData1()
console.log('res:',res);
}
foo1()
.catch(err=> {
console.log('err:',err);
})
ES9
async iterators: 迭代器补充
Object spread operators
Object Spread 运算符的基本思想是使用现有对象的自身属性来创建新的普通对象。 所以
{...obj}
创建一个和obj
具有相同属性的对象。 对于普通的旧 JavaScript 对象,你实际上是在创建一个obj
副本。
const obj = { foo: 'bar' };
const clone = { ...obj }; // `{ foo: 'bar' }`
obj.foo = 'baz';
clone.foo; // 'bar'
Promise finally: Promise补充
ES10
flat flatMap
flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
1.flatMap是先进行map操作,再做flat的操作;
2.flatMap中的flat相当于深度为1;
// flat的使用
const nums = [10, 20, [3, 4], [[40, 50], [18, 45]], 78, [55, 88]]
// 第一个参数,递归的深度,默认是为一
const newNums = nums.flat()
console.log(newNums) // [10, 20, 3, 4, [40, 50], [18, 45], 78, 55, 88]
const newNum2 = nums.flat(2)
console.log(newNum2) // [10, 20, 3, 4, 40, 50, 18, 45, 78, 55, 88]
// flatMap的使用 第一个参数是传进一个回调函数,第二个参数是传入一个this
const nums = [10 ,20, 30]
const newNum3 = nums.flatMap(item => {
return item * 2
}, {})
const newNum4 = nums.map(item => {
return item * 2
})
// 3. flatMap的应用场景
const message = ['Hello World', '你好啊 李焕英', 'my name is coder']
const words = message.flatMap(item => {
return item.split(' ')
})
console.log(words) // ['Hello', 'World', '你好啊', '李焕英', 'my', 'name', 'is', 'coder']
const words2 = message.map(item => {
return item.split(' ')
})
console.log(words2)
// [['Hello', 'World'], ['你好啊', '李焕英'], ['my', 'name', 'is', 'coder']]
Object fromEntries
const obj = {
name: 'coder',
age: 18,
height: 1.88
}
const entries = Object.entries(obj)
console.log(entries) // [['name', 'coder'], ['age', 18], ['height', 1.88]]
const newObj = {}
for(const entry of entries) {
newObj[entry[0]] = entry[1]
}
// ES10增长Object.fromEntries
const newObj = Object.fromEntries(entries)
console.log(newObj) // {name: 'coder', age: 18, height: 1.88}
// Object.fromEntries的应用场景
const queryString = 'name=coder&age=18&height=1.88'
const queryParams = new URLSearchParams(queryString)
const paramObj = Object.fromEntries(queryParams)
console.log(paramObj) // {name: 'coder', age: '18', height: '1.88'}
trimStart trimEnd(字符串去除前,后空格)
const message = ' Hello World '
console.log(message.trim()) // 'Hello World'
// 去除头部空格
console.log(message.trimStart()) // 'Hello World '
// 去除尾部空格
console.log(message.trimEnd()) // ' Hello World'
Symbol description
Symbol.prototype.description 获取symbol标识
let s = Symbol('标识')
s.description // 标识
Optional catch binding
ES11
BigInt(大数字)
// ES11之前 max_safe_integer
const maxInt = Number.MAX_SAFE_INTEGER
console.log(maxInt) // 9007199254740991
console.log(maxInt + 1) // 9007199254740992
console.log(maxInt + 2) // 9007199254740992
// ES11 之后 BigInt 数字在后面加上一个n代表大数字
const bigInt = 900719925474099200n
console.log(bigInt + 10n)
const num = 100
console.log(bigInt + BigInt(num))
?? (Nullish Coalescing Operator 空值合并运算)
// SE11: 空值合并运算 ??
let foo
console.log(foo) // undefined
cosnt fo = 0
// 逻辑或 0 '' 是为假
// const bar = fo || 'default value'
// 只有undefined 和 null 为假
const bar = fo ?? 'default value'
?.(OptionalChaining 可选链)
const info = {
name: 'coder',
friend: {
name: 'lilei',
girlFriend: {
name: 'hmm'
}
}
}
console.log(info.friend.girlFriend.name) // hmm
info.friend = null
// Cannot read properties of null (reading 'girlFriend')
console.log(info.friend.girlFriend.name)
// ES11之前的做法
if(info && info.friend && info.friend.girlFriend) {
console.log(info.friend.girlFriend.name)
}
// ES11 可选链 当friend没有,girlFriend没有,后面的取值就不会执行了
console.log(info.friend?.girlFriend?.name)
Global Object(获取全局对象)
// 获取某一个环境下的全局 对象(Global Object)
// 浏览器下
console.log(window)
console.log(this)
// 在node下
console.log(global)
// ES11 globalThis
console.log(globalThis) // 在不同环境下,获取的全局对象都是不一样的
for...in 标准化
const obj = {
name: 'coder',
age: 18
}
for(const item in obj) {
// ES11之前,不同浏览器,可能会获取到值
console.log(item) // ES11后标准化,获取的item就是对象的key
}
Dynamic import
promise.allSettled
import meta
ES12
FinalizationRegistry(引用不足)
当一个对象被强引用变量引用时,它处于可达状态,是不可能被垃圾回收器回收的,即使该对象永远不会被用到也不会被回收。
// ES12: FinalizationRegistry 类
// 回调函数,当注册的对象,没有被引用了,销毁的时候,就会执行这个回调函数
const finalRegistry = new FinalizationRegistry((item) => {
console.log('注册一个finalRegistry的对象,某一个被销毁')
})
let obj = {name: 'coder'}
const info = {age: 18}
finalRegistry.register(obj)
finalRegistry.register(info, 'value')
obj = null
WeakRef
// ES12: FinalizationRegistry 类
// 回调函数,当注册的对象,没有被引用了,销毁的时候,就会执行这个回调函数
const finalRegistry = new FinalizationRegistry((item) => {
console.log('注册一个finalRegistry的对象,某一个被销毁',item)
})
const info = {age: 18}
let info = new WeakSet() // 弱引用
info.add(obj)
// ES12 WeakRef类
/** WeakRef.prototype.deref : 如果源对象没有销毁,那么可以获取原对象,
如果销毁了获取的就是undefined **/
let info = new WeakRef(obj) // 弱引用
console.log(info.deref().name)
finalRegistry.register(info, 'value')
obj = null
逻辑赋值运算
||= (逻辑或赋值运算),&&=(逻辑与赋值运算) , ??= (逻辑空赋值运算)
// ||= 逻辑或赋值运算
let message = undefined
// message = message || 'default value'
message ||= 'default value'
// 2. &&= 逻辑与赋值运算
let info = {
name: 'coder'
}
// 判断info,有值的情况,取出info.name
info = info && info.name
info &&= info.name
// ??= 逻辑空赋值运算
let message = null
message = message ?? 'hello world'
message ??= 'Hello'
数字分隔符(_)
// 大数字文字很难使人眼快速解析
1000000000000 1019436871.42
1_000_000_000_000 1_019_436_871.42
String.replaceAll(字符串替换)
const str = 'aaabbbcccdddabcd'
str.replaceAll('b', 't')
console.log(str) // aaatttcccdddatcd