# 一、ESM 模块化
# 基础认知
若我们使用了type="module"
,则该脚本文件将被当作模块(一个文件就是一个模块),其顶层代码在模块作用域内执行,而不是在全局作用域内执行。
<script type="module" src="main.js">
console.log(this) // undefined
console.log(import.meta.url) // http://localhost/main.js
</script>
# 1、严格模式
模块内部的顶层变量和函数,默认是严格模式
严格模式
严格模式(strict mode)是一种特殊的运行模式,它使得 JavaScript 变得更加严格,更加符合语言的精神。
严格模式主要有以下限制:
显示报错:
- 只读属性不可写
- 函数的参数不能有同名参数
安全提示:
- 变量必须声明后再使用
- 禁止 this 指向全局对象
- 禁止删除变量
# 2、多次导入
多次导入仅执行第一次导入(存在共享机制功能,但不推荐)
// 1.js
import { admin } from './admin.js'
admin.name = 'Pete'
// 2.js
import { admin } from './admin.js'
alert(admin.name) // Pete
// 1.js 和 2.js 引用的是同一个 admin 对象
// 在 1.js 中对对象做的更改,在 2.js 中也是可见的
# 3、异步加载
- one.js
// DOM操作
const domT = document.querySelector('div')
console.log(domT)
# 4、模块作用域
模块内部的顶层变量和函数,外部无法访问。
# 二、Proxy 对象
Proxy 对象的所有用法都有一个共同的结构:
const proxy = new Proxy(target, handler)
# 1、实例方法
Proxy 对象的方法有很多,这里只介绍几个常用的:
更多 Proxy 实例的方法(主要起拦截作用):参考Proxy 对象 (opens new window)
- get():拦截对象属性的读取,比如
proxy.foo
和proxy[foo]
。 - set():拦截对象属性的设置,比如
proxy.foo = v
或proxy[foo] = v
。 - deleteProperty():拦截
delete
运算符,比如delete proxy.foo
。
- defineProperty():拦截
Object.defineProperty()
、Object.defineProperties()
,返回一个布尔值以确定是否允许该属性的添加或修改。
其他方法
- apply():拦截函数的调用,比如
proxy(...args)
、proxy.call(obj, args)
、proxy.apply(obj, args)
。 - has():拦截
in
运算符,比如prop in proxy
。 - ownKeys():拦截
Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
、Object.keys()
,返回一个数组以确定对象的属性名。 - getPrototypeOf():拦截
Object.getPrototypeOf()
,返回一个对象以确定对象的原型。 - setPrototypeOf():拦截
Object.setPrototypeOf()
,返回一个布尔值以确定是否允许设置对象的原型。 - isExtensible():拦截
Object.isExtensible()
,返回一个布尔值以确定对象是否可扩展。 - preventExtensions():拦截
Object.preventExtensions()
,返回一个布尔值以确定是否允许扩展对象。 - getOwnPropertyDescriptor():拦截
Object.getOwnPropertyDescriptor()
,返回属性的描述对象。
Proxy 和 defineProperty 的区别
- Proxy 对象可以拦截一些 Object 方法
- defineProperty 是众多 Object 的方法之一
# 2、defineProperty
-Object.defineProperty()
方法可以直接在一个对象上定义一个新属性,或者修改一个已有属性,并返回这个属性。
Object.defineProperty(obj, prop, descriptor)
应用:数据代理
描述:
通过一个对象obj1
代理对 另一个对象obj2
中的属性进行操作(读/写)
// 一个对象
let obj1 = {
title: '数据代理'
}
// 另一个对象
let obj2 = {}
// 数据代理🤔
Object.defineProperty(obj2, 'title', {
get() {
return obj1.title
},
set(newValue) {
obj1.title = newValue
}
})
效果:
obj2 可以对 obj1 中的 title 属性进行操作
应用:数据劫持(响应式更新)
常规的,我们想要达到数据同步更新,则需要手动更新数据,非常不方便
如果使用 Object.defineProperty 实现同样的效果则简单的多
let numb = 18
let person = {
name: '张三',
set: '男',
age: numb
}
// 修改数据
numb = 20
console.log(person.age) // 18
// 1、手动更新
// person.age = numb // 手动更新
// 2、自动更新
Object.defineProperty(person, 'age', {
// 读取person.age时触发
get: function () {
console.log('读取person.age')
return numb
},
// 修改person.age时触发
set: function (newValue) {
console.log('person.age被修改了')
numb = newValue // 同步🎈更新到numb
}
})
console.log(person.age) // 20
效果:
现用现取
let obj = {
name: '张三'
}
Object.defineProperty(obj, 'age', {
value: 20,
writable: true,
enumerable: true,
configurable: true
})
console.log(obj.age) // 20
# 2、new Proxy
const obj = {
name: '张三',
age: 20
}
const handler = {
get(target, prop) {
console.log(`读取了${prop}属性`)
return target[prop]
},
set(target, prop, value) {
console.log(`修改了${prop}属性,新的属性值为${value}`)
target[prop] = value
},
deleteProperty(target, prop) {
console.log(`删除了${prop}属性`)
delete target[prop]
}
}
const proxy = new Proxy(obj, handler)
console.log(proxy.name) // 读取了name属性
console.log(proxy.age) // 读取了age属性
# 5、数据劫持 ✨
当我们想要对重新设置的内容进行拦截过滤时,我们就可以用到 proxy
- Object.defineProperty 方式
完整示例
Object.defineProperty(person, age, {
get() {
return age
},
set(newVal) {
age = newVal
}
})
let person = {
age: 21
}
// 单个属性
let prop_proxy = person.age
Object.defineProperty(person, 'age', {
get() {
return prop_proxy
},
set(newVal) {
prop_proxy = newVal < 0 ? 0 : newVal > 150 ? 150 : newVal
}
})
// module.exports = prop_proxy
module.exports = person
- Proxy 对象方式
完整示例
person = new Proxy(person, {
// 读取(查)
get(target, key) {
return target[key]
// return Reflect.get(target, key)
},
set(target, key, newVal) {
target[key] = newVal
// Reflect.set(target, key, newVal)
}
})
let person = {
age: 21
}
// 整个对象(快捷)
person = new Proxy(person, {
set(target, key, newVal) {
Reflect.set(target, key, newVal < 0 ? 0 : newVal > 150 ? 150 : newVal)
}
})
module.exports = person
# 三、Reflect 映射
更多 Reflect 实例的方法:参考Reflect 对象 (opens new window)
# 1、基本认知
Reflect.get、Reflect.set、Reflect.delete、Reflect.has 等
let person = {
username: 'ren',
age: 21
}
// 原先
'assign' in Object
delete obj.name
console.log(person.age)
// 现在
Reflect.has(Object, 'assign')
Reflect.deleteProperty(obj, 'name')
console.log(Reflect.get(person, 'age'))
# 2、优势介绍
- 问题展示
let person = {
username: 'ren',
age: 21
}
Object.defineProperty(person, 'sex', {
get() {
return '男'
}
})
// 报错:Uncaught TypeError: Cannot redefine property: sex
Object.defineProperty(person, 'sex', {
get() {
return '女'
}
})
提示
现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
- 解决问题
const result1 = Reflect.defineProperty(person, 'sex', {
get() {
return '男'
}
}) // true
const result2 = Reflect.defineProperty(person, 'sex', {
get() {
return '女'
}
}) // false
console.log(person.sex) // '男'
# 3、应用:异常处理 🤔
- 旧写法
// 异常处理
try {
Object.defineProperty(target, property, {
//
})
Object.defineProperty(target, property, {
//
})
} catch (err) {
//
}
- 新写法
const result1 = Reflect.defineProperty(person, 'sex', {
get() {
return '男'
}
}) // true
const result2 = Reflect.defineProperty(person, 'sex', {
get() {
return '女'
}
}) // false
// 异常处理
if (result1) {
//
} else {
//
}
# 四、异步编程
# 1、Promise
Promise 是异步编程的一种解决方案,它代表一个异步操作的最终完成或失败,并将其结果传给回调函数。
- 基本用法
// 创建一个Promise实例
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
resolve('success')
}, 1000)
})
// 异步操作成功时调用resolve,失败时调用reject
promise
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
- 链式调用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise
.then((res) => {
console.log(res)
return 'hello'
})
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
- Promise.all()
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1 success')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 success')
}, 2000)
})
Promise.all([p1, p2])
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
# 2、async/await
async/await 是基于 Promise 的语法糖,它使异步操作变得更加方便。
- 基本用法
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const data = await response.json()
console.log(data)
} catch (error) {
console.log(error)
}
}
fetchData()
- 链式调用
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const data = await response.json()
console.log(data)
return data
} catch (error) {
console.log(error)
}
}
fetchData()
.then((data) => {
console.log(data)
})
.catch((error) => {
console.log(error)
})
- 并发执行
async function fetchData() {
try {
const [response1, response2] = await Promise.all([
fetch('https://jsonplaceholder.typicode.com/todos/1'),
fetch('https://jsonplaceholder.typicode.com/todos/2')
])
const [data1, data2] = await Promise.all([response1.json(), response2.json()])
console.log(data1, data2)
} catch (error) {
console.log(error)
}
}
fetchData()
# 五、Generator 函数
关于 Generator 函数的详细解读,可以参考
- generator 使用
function* getBooksAPIFn() {
let res1 = yield ajax(`http://www.liulongbin.top:3006/api/getbooks?author=${name}`)
console.log('请求一的结果', res1)
let res2 = ajax(`http://www.liulongbin.top:3006/api/getComment?id=${res[0].id}`)
console.log('请求一的结果', res2)
}
// 手动实现
let g = getBooksAPIFn()
g.next().value.then((data) => {
g.next(data).value.then((res) => {
g.next(res)
})
})
// 自动实现
function AutoRun(gen) {
let g = gen()
function next(data) {
let res = g.next(data)
if (res.done) return
res.value.then(function (data) {
next(data)
})
}
next()
}
AutoRun(getBooksAPIFn)
- async / await 升级
async function getBooksAPIFn() {
var res1 = await ajax(`http://www.liulongbin.top:3006/api/getbooks?author=${name}`)
var res2 = ajax(`http://www.liulongbin.top:3006/api/getComment?id=${res[0].id}`)
return res2
}
getBooksAPIFn()
.then((res2) => {
console.log('ajax请求成功', res2)
})
.catch((err) => {
console.log('ajax请求失败', err)
})