# 一、ESM 模块化

# 基础认知

  若我们使用了type="module",则该脚本文件将被当作模块(一个文件就是一个模块),其顶层代码在模块作用域内执行,而不是在全局作用域内执行。

推荐阅读现代 JavaScript 教程 (opens new window)

<script type="module" src="main.js">
  console.log(this) // undefined

  console.log(import.meta.url) // http://localhost/main.js
</script>

# 1、严格模式

  模块内部的顶层变量和函数,默认是严格模式

推荐阅读WangDoc-JavaScript-严格模式 (opens new window)

严格模式

  严格模式(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.fooproxy[foo]
      • set():拦截对象属性的设置,比如proxy.foo = vproxy[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)
        })
      
      更新于 : 8/7/2024, 2:16:31 PM