先来几个案例:
1. 读取一个被连接对象的深层次的属性的值
const user = {
address: {
street: 'xx街道',
getNum() {
return '100号'
}
}
}
const street = user && user.address && user.address.street
const num = user && user.address && user.address.getNum && user.address.getNum()
ES6+ 语法:
const street = user?.address?.street
const num = user?.address?.getNum?.()
2.消除魔术字符串(新增的数据类型)
const getArea = (shape) => {
let area = 0;
switch (shape) {
case 'triangle':
area = 1
break
case 'circle':
area = 2
break
}
return area
}
getArea('triangle')
ES6+ 语法:
const shapeType = {
triangle: Symbol(),
circle: Symbol()
}
function getArea(shape) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = 1
break
case shapeType.circle:
area = 2
break
}
return area
}
3. 将类数组转化为数组
const arrayLike = document.querySelectorAll('.item')
ES5 语法:
Array.prototype.slice.call(arrayLike)
ES6+ 语法:
Array.from(arrayLike)
Array.from('1234567')
Array.from(new Set([1,2,3,1]))
4. 优雅的异步操作与结构赋值
function getData (url) {
ajax(url, function(res) {
var data = res.data
this.list = data
})
}
ES6+ 语法:
async function getData (url) {
const {data} = await axios.get(url)
this.list = data
}
ES6(2015)
新的声明方式: let const
-
不存在变量提升
-
不属于顶层对象window
-
不允许重复声明
-
块级作用域
if(true) var a=5 if(true) let a= 5 // error 加大括号即可
for(var i= 0; i<3; i++) { setTimeout(function() { console.log(i) }) }
// 3个3
for(var i= 0; i<3; i++) { (function(j){ setTimeout(function() { console.log(j) }) })(i) }
// 0 1 20
for(let i= 0; i<3; i++) { setTimeout(function() { console.log(i) }) }// 0 1 2
结构赋值
-
按照一定模式, 从数组和对象中提取值, 对变量进行赋值
-
数组结构
-
对象结构
-
字符串结构
let json = '{"a":"hello", "b":"world"}' let {a, b} = JSON.parse(json) // hello world
ES5中数组遍历方式
-
for循环
-
forEach(): 没有返回值, 只是针对每个元素调用func
-
map(): 返回新的array, 每个元素为调用func的结果
-
filter(): 返回符合func条件的元素数组
-
some(): 返回boolean, 判断是否有元素是否符合func 条件
-
every(): 返回boolean, 判断每个元素是否符合func条件
-
reduce(): 接受收一个函数作为累加器
-
for in 下标 for of 内容
let arr = [1, 2, 3, 3] let sum = arr.reduce(function(prev, cur, index, array){ return prev + cur }, 0) console.log(sum) //6
let max = arr.reduce(function(prev, cur) { return Math.max(prev, cur) }) console.log(max) // 3
let res = arr.reduce(function(prev, cur) { prev.indexOf(cur) == -1 && prev.push(cur) return prev }, []) console.log(res) // [1,2,3]
ES6 中的数组遍历
- find: 返回第一个通过测试的元素
- findIndex: 返回第一个通过的元素的下标
- for of
values()
-
keys()
-
entries()
let res = arr.find(function(value) { return value = 8}) for(let i of arr.keys()) 返回index for(let i of arr.values()) 返回value for(let [index, item] of arr.entries())
数组的扩展
- 类数组/伪数组
**Array.from
**
-
ES5: let arr = Array.prototype.slice.call(div3) ES6: let arrayLike = { '0': 'es6', '1': 'es7', '2': 'es8', length: 3 } let arr = array.from(arrayLike)
Array.of
let arr = new Array(1,2) // 数组长度为2
let arr = new Array(3) // 一个值时代表定义数组的长度
let arr = Array.of(3)
let arr = Array.of(1, true, 'imooc', [1,2], {name: 'oyyf'}) // 场景: 组装不同类型的数据为一个数组
**copeWithin () ** // 三个参数, 1代表从那个位置开始替换数据 必选
2代表开始哪个位置读取参数 可选
3从哪个位置停止读取参数 可选, 默认为末尾
let arr = [1, 2, 3, 4, 5]
arr.copyWithin(1, 3)
fill() // 三个参数, 1是代表填充的参数 2代表 从哪个位置填充 3从哪个位置截止填充
let arr = new Array(3).fill(7) // [7, 7, 7]
let arr = [1,2,3,4,5].fill('oyyf', 1, 3) // [1, "oyyf", "oyyf", 4, 5]
includes()
let arr = [1,2,3,NaN]
cosnole.log(arr.indexOf(NaN)) // -1, 包含返回下标, 不包含返回-1
cosnole.log(arr.includes(NaN)) // true
函数的参数
-
参数默认值, 参数的默认值放在最后面
-
与解构赋值结合({x='', y=''} = {})
-
length 的属性 返回没有指定默认值的参数个数
-
作用域
let x = 1 function foo(y=x) { let x= 2 console.log(y) } foo() // 1
5. 函数的name 属性
console.log((new Function).name) // anonymous
function foo(x, y) {
console.log(this, x, y) // {name: 'oyyf'}, 1, 3
}
foo.bind({name: 'oyyf'}, 1, 3)()
console.log(foo.bind({}).name) // bound foo
扩展运算符与rest参数
扩展运算符: 把数组或者类数组展开用逗号隔开的值
rest参数: 把逗号隔开的值组合成一个数组
箭头函数
this指向定义时所在的对象, 而不是调用时所在的对象
不可以当作构造函数
不可以使用arguments对象
对象的扩展
属性简洁写法
let name = 'oyyf'
let age = 18
let s = 'company'
let obj = {
name,
age,
[s]: 'imooc', // 动态的用数组包裹
study(){
ccosnole.log(this.name + '正在xxx')
}
}
Object.is() 判断左右是否严格相等
Object.is(NaN, NaN) // true 对象的比较比较的是内存地址
let obj1 = {
name: '111'
}
let obj2 = {
name: '111'
}
Object.is(obj1, obj2) // false
in
let arr = [1, 2, 3]
console.log(3 in arr) // false 数组时判断是下标
深拷贝和浅拷贝
. 如何把一个对象复制给另一个对象
. Object.assign() // 不安全:
let target = {
a: { b: {
c: 1
},
e: 2,
f: 2,
g: 6
}}
let source = {
a: {
b: {
c: 1
},
e: 2,
f: 2
},
}
Object.assign(target, source) // g 消失了
// 深拷贝 互不干扰
let a = 5
let b = a
a = 6
cosnole.log(a, b) // 6, 5
// 浅拷贝 相互改变
let obj1 = {
name: "oyyf",
age: 25
}
let obj2 = obj1
obj1.age = 18
console.log(obj1) // 18
cosnole.log(obj2) // 18
let obj1 = {
name: 'oyyf',
age: 25
}
// "{"a": 'hello', "b": "world"}"
// JSON.stringify() 对象转为json 格式字符串
// JSON.parse() json格式字符串转为对象
. 实现一个深拷贝
let str = JSON.stringify(obj1)
let obj2 = JSON.parse(str)
obj1.age = 18
console.log(obj2) // 25
let checkType = data => {
// typeof 基本数据类型
return Object.peototype.toString.call(data).slice(8, -1)
}
let deepClone = target => {
let targetType = checkType(traget)
let result
if (targetType === 'Object') {
result = {}
} else if(targetType === 'Array') {
result = []
} else {
return target
}
for(let i in target) {
let value = target[i]
let valueType = checkType(value)
if (valueType === 'Object' || valueType === 'Array') {
result[i] = deepClone(target[value])
} else {
result[i] = value
}
}
return result
}
let arr1 = [1, 2, {age: 18}]
let arr2 = deepClone(arr1)
arr2[2].age = 34
面向过程和面向对象(JavaScript是一种基于对象(object-based)的语言)
把大象装进冰箱需要几步:
面向过程:
1, 打开门
2, 装进去
3, 关门
面向对象: (JAVA Everything is Object)
1. 大象
2. 冰箱
3.隐藏对象: 一双手/一个人来装大象
类与对象
人 欧阳月飞
类(class)是对象(object)的模版, 定义了同一组对象共有的属性和方法
Es5中的类与继承
fucntion People(name, age) {
this.name = name
this.age = age
}
let p1 = new People('111', '18')
ES7
数组的扩展: incudes
const arr = ['es6', 'es7', 'es8']
cosnole.log(arr.inculdes('es7')) // true
// includes -> boolean
两个参数(要搜所的值, 索引)
console.log(arr.includes('es7', 1))
console.log(arr.includes('es7', -1)) // false
与indexOf区别(索引, 下表), includes更严格
arr.indexOf('es7') // 1
arr.indexOf('es7') > -1 // true
const arr = ['es6', NaN, 'es7', 2]
arr.includes(NaN) // true
arr.indexOf(NaN) // -1
arr.includes('2') // false
数值扩展
.幂运算符: **
.等同于 Math.pow(底数, 指数)
2 ** 10 // ** 之间不能有空格,否则语法报错
ES8(2017)
async(异步)/await (等待)
function timeout() {
return new Promise(resolve => {
setTimeout(() => {
cosnole.log(1)
resolve(1)
}, 1000)
})
}
async function foo() {
const res = await timeout()
console.log(res)
console.log(2)
}
foo()
对象扩展
.Object.values()
.Object.entries()
const obj = {
name: 'oyyf',
web: 'www.baidu.com',
course: 'es'
}
const res = Object.keys(obj).map(key => obj[key])
// 等同于
Object.values(obj)
Object.entrise(obj) // 二维数组 [key, value]
对象属性描述符号
.Object.getOwwnPropertyDescriptors()
const obj = {
name: 'oyyf',
course: 'es
}
console.log(Object.getOwwnPropertyDescriptors(obj))
{
name: {
value: 'oyyf', // 值
writable: true, // 是否可更改
enumerable: true, // 是否可以for in 循环
configurable: true // 能否用delete 运算符删除
}
}
const obj = {}
Reflect.defineProperty(obj, 'name', {
value: 'oyyf',
writable: false,
configurable: true,
enumerable: true
})
字符串扩展
.String.prototype.padStart()
.String.prototype.padEnd()
const str = 'oyyf'
str.padStart(8, 'x') // 目标长度, 以什么填充 xxxxoyyf
// 使用场景: yyyy--mm-dd 2020-04-01 xxxxxxx6789 身份证号 时间戳
const now = new Date()
const year = now.getFullYear()
cosnt month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = (now.getDate()).toString().padStart(2, '0')
const tel = '17682342970'
tel.splice(-4).padStart(tel.length, 'x')
尾逗号(允许函数参数列表使用尾逗号)
fucntion foo (a, b, c,) {
consoel.log(a, b, c)
}
foo(4, 5, 6, )
作用: 减少修改代码行数
ES9(2018)
异步迭代
.for-await-of(多个异步操作)
Symbol.asycIterator(异步迭代器)
const arr = [1, 2, 3, 4]
arr[Symbol.iterator] = fucntion() {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ?
{
value: arr[nextIndex++],
done: false
}:{
value: undefined,
done: true
}
}
}
}
for(let item of arr) {
}
}
fucntion getPromise(time) {
return new Promise(res, rej) => {
setTimeout(() => {
res({
value: time,
done: fasle
})
}, time)
}
}
const arr = [getPromise(1000), getPromise(2000), getPromise(3000)]
arr[Symbol.asyncIterator] = fucntion() {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ? arr[nextIndex++] : Promise.resolve({
value: undefined,
done: true })
}
}
}
async fucntion test(){
for await(let item of arr) { consoel.log(item) }
}
正则表达式扩展
.dotAll
.具名组匹配
.后行断言
// dot
const reg= /./ /./s
reg.test('5') true true
reg.test('x') true true
reg.test('\n') false true
reg.test('\r') false true
reg.test('\u{2028}') false true
// g 全局 i忽略 m 跨行匹配 es5
// y 粘性 u 匹配unicode s 匹配任意单个字符 es6
// 具名组匹配
const date = /(\d{4}) - (\d{2}) -(\d{2})/.exec('2020-01-01')
date[1/2/3] 年月日 // low groups是undeined
const reg = const date = /(?<year>\d{4}) - (?<month>\d{2}) -(?<day>\d{2})/
const groups = reg.exec('2020-12-06').groups
const {year, month, day} = groups
// 后行断言 先行断言
const str = 'ecmascript'
str.match(/ecma(?=script)/) // 先行断言, 匹配后边必须是script的字符串
str.match(/(?<=ecma)script/) // 后行断言, 必须后边是script, 前边是ecma
str.match(/(?<!ecma)script/)
对象扩展 rest
const arr1 = [1, 2, 3 ]
const arr2 = [4, 5, 6 ]
const arr3 = [...arr1, ...arr2]
const obj1 = {
name: 'oyyf',
age: 25
}
const obj2 = {
school: 'xxxx'
}
const obj3 = {...obj1} // 浅拷贝
obj1.age = 18
const obj4 = {...obj1, ...obj2}
const {name, age, ...resr} = obj4 // 必须放在后边
Promise扩展 finally()
new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('finally')
})
使用场景: 不管请求是否成功, loading 消失
字符串扩展 (放宽了限制)
const foo = (a, b, c, d) => {
console.log(a,b, c, d)
// ['这是', ', 他的年龄是']
// oyyf
// 25
// undefined
}
const name = 'oyyf'
const age = 25
foo`这是${name}, 他的年龄是${age}`
const foo = arg => {
console.log(arg)
}
foo`\u{61} and \unicode` // es9 之前会报unicode无效的转译序列
let str = `\u{61} and \unicode` // 报错
ES10(2019)
对象扩展
.Object.fromEntries()
const obj = {
name: 'oyyf',
course: 'es'
}
const entries = Object.entries(obj)
// [["name": 'oyyf'], ['course': 'es']]
Object.fromEntries(entries)
// [name: 'oyyf', course: 'es']
使用场景: map-> 对象
字符串的扩展
String.prototype.trimStart() ; 消除字符串前面的空格
String.prototype.trimLeft()
String.prototype.trimEnd(): 消除字符串后面的空格
String.prototype.trimRight()
const str = ' oyyf '
str.replace(/^\s+/g, '')
str.replace(/\s+$/g, '')
str.trim()
数组的扩展
Array.prototype.flat()
Array.prototype.flatMap()
const arr = [1, 2, [3, 4, [5, 6, [7, 8, 9]]]]
console.log(arr.flat()) // 默认参数是1, 只会拍平一级
console.log(arr.flat(Infinity)) // 无限的
const arr = [1, 2, 3, 4, 5]
const res = arr.map(x => x + 1)
const res = arr.map(x=>[x+1]).flat()
const res = arr.flatMap(x => [x+1])
修改Function.prototyype.toString() 返回源代码中的实际文本片段
function foo() {
console.log('oyyf')
}
foo.toString() // 方法文本化输出
可选的Catch Binding 省略catch 绑定的参数和括号
const validJSON = json => {
try{
JSON.parse(json)
return true
}.catch {
return false
}
}
const json ='{name": 'oyyf'}'
validJSON(json)
JSON扩展
.JSON.superset
.JSON.stringify() 增强能力
eval('var str="oyyf";\u2029 function foo(){retuurn str}') // 段分隔符
console.log(foo()) // oyyf
// 0xD800~0xDfff
JSON.stringify('\uD83D\uDE0E') // emoji
Symbol 扩展
const s= Symbol('oyyf') // description 可读不可写
s.description // oyyf
const s2 = Symbol()
s2.description // undefined
ES11(2020)
字符串扩展 matchAll()
const str = `
<html>
<body>
<div>111</div>
<p>222</p>
<div>11122</div>
<span>222</span>
</body>
</html>
`
// exec g
function selectDiv(regExp, str) {
let matches = []
while(true) {
const match = regExp.exec(str)
if (match == null) {
break
}
matches.push(match[1])
}
return matches
}
const regExp = /<div>.(*)<\/div>/g
const res = selectDiv(resExp, str)
// match
str.match(regExp)
// replace
function selectedDiv(regExp, str) {
let matches = []
str.replace(regExp, (all, first) => {
matches.push(first)
})
return matches
}
selectDiv(regExp, str)
// matchAll
function selectedDiv(regExp, str) { let matches = []
for (let match of str.matchAll(regExp)) {
matches.push(match[1])
}
return matches
}
Dynamic Import (动态导入/按需导入)
const oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', () => {
import('./ajax').then(mod => {
mod.default('static/a.json', res => {
console.log(res)
})
})
})
新的原始数据类型 BigInt
const max= 2 ** 53 9007199254740992
Number.MAX_SAFE_INTEGER 9007199254740991
max === max + 1 // true
const bigInt = 9007199254740993n
typeof bigInt // bigInt
1n == 1 // true
1n === 1 // false
const bigInt2 = BigInt(9007199254740993n)
const num = bigInt + bigInt2
num.toString() // 以字符串才可以存储这么大的值
Promise 扩展
.Promise.allSettled() // 稳定的固定的
.allSettled vs all
Promise.all([
Promise.resolve({code: 200, data: [1, 2, 3]}),
Promise.reject({code: 500, data: []},
Promise.resolve({code: 200, data: [7, 8, 9]})
]).then(res => {
}).catch(err => {
//
})
Promise.allSettled([
Promise.resolve({code: 200, data: [1, 2, 3]}),
Promise.reject({code: 500, data: []},
Promise.resolve({code: 200, data: [7, 8, 9]})
]).then(res => {
//
res.filter(item => item.status === 'fulfilled')
}).catch(err => {
})
globalThis: 提供了一个标准的 方式 去获取不同环境下的全局对象
// node: global
//web: window self
const getGlobal = () => {
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
throw new Error('无法获取全局对象')}
globalThis getGlobal() // window
Optional chaining 可选链
const user = {
address: {
street: 'xx街道',
getNum() {
return '80号'
}
}
}
const street = user?.address?.street
空值合并运算符
const b = 0 null undefined '' false
const a = b || 5
console.log(a) // 5
const a = b ?? 6
console.log(a) // null / undefined 才取后边的