Es6之代理与模块

2 阅读8分钟

Es6之代理与模块

Proxy代理对象

在学习之前我们需要了解一下什么是proxy

Proxy(代理)是 ES6 中引入的一个非常强大的特性,它允许你创建一个对象的“代理”,从而可以拦截并重新定义该对象的基本操作(如属性查找、赋值、枚举、函数调用等)。

你可以把它理解为一个对象的“中介”或“包装器”。所有对目标对象的操作,都不会直接作用到对象本身,而是先经过这个“中介”。这个“中介”可以决定如何以及是否将操作转发给原始对象。

我举个例子你就知道了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./main.js"></script>
    <link rel="stylesheet" href="main.css">
    <link rel="shortcut icon" href="#"/>
</head>
<body>
    <div id="app">1111</div>
</body>
</html>

这是一个很简单的页面 只显示了一个1111

在我引用的main.js文件 我这样子写

const obj = {
	name:'pzt',
    age = '19'
}
const app = document.getElementById('app')
app.textContent = obj.name

那么现在因为我写了这一段js index页面就会把1111改成pzt

那么现在当我修改了obj的name时

obj.name = 'zx'

但是我修改完后 index页面还是呈现的是pzt

所以我只能再加上一句

app.textContent = obj.name

才能使得index页面呈现zx

但是在现实中这样子就太麻烦了 我总不可能每一次修改名字后都要加上这么一句吧

那么这个时候 proxy代理对象的作用就体现出来了 我可以创建一个proxy来代理obj这个对象 然后在该proxy中添加我想要的效果 比如我想让index页面在我修改完名字之后显示最新的名字 我就可以在该proxy中添加这一句话

app.textContent = obj.name

就可以使得每次我修改name时都会自动执行该命令 多说无益 直接上代码解释

但是首先你要了解proxy的基本语法

创建一个 Proxy 需要两个参数:

  1. target(目标对象): 要被代理的原始对象。
  2. handler(处理器对象): 一个定义了哪些操作将被拦截以及如何重新定义这些操作的对象。处理器对象内部可以包含一系列可选的“捕获器”(trap)方法,比如 get, set, has
const target = { 
  message: "hello, world" 
};

const handler = {
  get(target, prop, receiver) {
    return ` intercepted get: ${target[prop]}`;
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.message); // 输出: intercepted get: hello, world
console.log(target.message); // 输出: hello, world (原始对象不受影响)

在上面的例子中,当我们通过 proxy 访问 message 属性时,handler 中的 get 捕获器被触发,它拦截了这次读取操作并返回了我们自定义的值。而直接访问原始 target 对象则不受影响。

常见的捕获器 (Trap)

处理器对象 handler 可以定义的方法非常多,这里列举一些最常用的:

捕获器名称对应的操作触发条件
get[[Get]]读取属性,如 proxy.property
set[[Set]]设置属性,如 proxy.property = value
has[[HasProperty]]in 操作符,如 'property' in proxy
deleteProperty[[Delete]]delete 操作符,如 delete proxy.property
apply[[Call]]函数调用,如 proxy(...args)
construct[[Construct]]new 操作符,如 new proxy(...args)
getPrototypeOf[[GetPrototypeOf]]Object.getPrototypeOf(proxy)
setPrototypeOf[[SetPrototypeOf]]Object.setPrototypeOf(proxy)
ownKeys[[OwnPropertyKeys]]Object.keys(proxy), Object.getOwnPropertyNames()

我直接复制了官方文档的一些内容下来给你看 你可能有些蒙蒙的 没事 我以刚刚修改名字的那个例子给你讲解一遍

const obj = {
    name : 'pzt',
    age : 18
}
					//这里写你要让p1代理的目标(就是这个obj)
const p1 = new Proxy(obj,{
    	//这里要注意target就是obj 但是你只能写出target
    	//prop就是该目标的属性
    	//receiver是最初调用该属性的对象,决定了函数中 this 的指向
	get (target , prop , receiver){
        return target[porp]
    },//get就是我刚刚所展现给你看的捕获器
    
					//注意 这里的value参数表示要赋给属性的新值 它就是等号右边的值
    set(target , prop , value ,receiver){
		target[porp] = value
        app.textContent = obj.name//看见没 最关键的来了 我刚刚是不是想让他实时更新最新的	属性 那么将捕获器捕获到你要修改的步骤时 他会执行这个set函数 所以就可以在index页面呈现我想要的效果
    }
})

p1.name = 'zx'//注意 这个时候修改的时候就不要直接用obj.name = 'zx' 这样子进行修改 你要使用	p1这个已经代理obj对象的Proxy实例来修改 才能有效

这个时候index页面就会呈现

zx

这个时候你可能会说 哎呀不就整个更新名字吗 有必要那么麻烦吗 我就想跟上面那样直接写一句话就行了还整这些 不 兄弟 你在业务中会遇到那么简单的逻辑吗 我只是为了方便让你懂才这样 所以学习这个是很必要的

Module模板

首先我们了解一下这个是什么意思

模块是 JavaScript 中一个非常重要的概念,它允许你将代码分割成独立的功能单元,每个单元拥有自己的作用域,并且可以导出(export)特定的变量、函数或类供其他模块使用,也可以导入(import)其他模块提供的功能。

通俗地来讲呢 你就想象一下 一个js模块 就是一个装满工具的工具箱

  • 你的代码文件(比如 myTools.js:就是一个工具箱
  • 里面的函数和变量(比如 hammer, screwdriver:就是箱子里的工具
  • export(导出):就是在箱子上贴个清单,告诉别人这个箱子里有什么工具可以借给别人用。
  • import(导入):就是你看到清单,去别人的箱子里把工具拿过来给自己用

1. 怎么把工具借出去?(Export 导出)

情景一:现场贴标签(声明时直接导出) 你在造工具的时候,直接就贴上“可外借”的标签。

// myTools.js 工具箱
export const hammer = "🔨 锤子"; // 造了个锤子,直接标明可外借
export function screwdriver() { // 造了个螺丝刀,直接标明可外借
    return "🪛 螺丝刀";
}

情景二:最后统一列清单(最后统一导出) 你先在箱子里把工具都造好,最后再统一列一个清单。

// myTools.js 工具箱
const hammer = "🔨 锤子"; // 造了个锤子,先放箱子里
const screwdriver = () => "🪛 螺丝刀"; // 造了个螺丝刀,先放箱子里

// 最后,列一个清单,写上哪些工具可以借
export { hammer, screwdriver };

情景三:镇箱之宝(默认导出) 一个工具箱里,可以有一个最常用、最主要的“镇箱之宝”。别人来借的时候,不用指定名字,直接说“我要借你的镇箱之宝”就行。

// myTools.js 工具箱
const powerDrill = "🪚 电钻"; // 这是我最牛的工具

// 把它设为“镇箱之宝”
export default powerDrill;

// 你也可以外借其他普通工具(命名导出)
export const hammer = "🔨 锤子";

记住:一个工具箱里,只能有一个“镇箱之宝”(默认导出)。


2. 怎么去借工具?(Import 导入)

情景一:借特定的工具(按名字借) 你看到别人工具箱的清单,只借你需要的那个工具。必须用 {} 包起来

// myProject.js 我的项目
import { hammer } from './myTools.js'; // 从myTools箱子里只借hammer

console.log(hammer); // 使用借来的锤子
// 输出: "🔨 锤子"

你可以一次借多个:

import { hammer, screwdriver } from './myTools.js';

情景二:借“镇箱之宝”(默认导入) 借镇箱之宝最简单,不用记名字,也不用 {}。你可以随便给它起个名字

// myProject.js 我的项目
import MyMainTool from './myTools.js'; // 把“镇箱之宝”借过来,我管它叫MyMainTool

console.log(MyMainTool); // 使用电钻
// 输出: "🪚 电钻"

情景三:把整个工具箱都搬过来 如果你需要很多工具,可以干脆把整个箱子都搬过来,然后用 箱子.工具名 的方式来用。

// myProject.js 我的项目
import * as ToolBox from './myTools.js'; // 把整个myTools箱子搬过来,我管这个箱子叫ToolBox

console.log(ToolBox.hammer); // 用箱子里的锤子
console.log(ToolBox.screwdriver()); // 用箱子里的螺丝刀

情景四:既借镇箱之宝,也借普通工具

// myTools.js 工具箱里有:默认导出的 powerDrill 和命名导出的 hammer
import MyDrill, { hammer } from './myTools.js'; 
// MyDrill 是镇箱之宝(电钻),{ hammer } 是普通工具(锤子)

为什么要用模块?(好处)

  1. 不打架:每个工具箱里的工具都是自己私有的,就算你和别人都有叫“hammer”的工具,也不会冲突。避免了“全局变量污染”。
  2. 好管理:代码变得非常有条理。工具(功能)都分门别类放在不同的箱子(文件)里,找起来方便。
  3. 可复用:写好一个工具箱(比如一个数据处理模块),可以在很多个项目里重复使用,不用重复造轮子。
  4. 安全:工具箱里你没答应外借 (export) 的工具,别人是拿不走的,是私有的。

在网页里怎么用?

在 HTML 里引入你的主模块文件时,要加上 type="module" 这个属性,告诉浏览器:“这是个模块哦!”

html

<script type="module" src="myProject.js"></script>

重要提醒:带有 type="module" 的脚本不能直接用浏览器打开本地文件(file:// 协议),需要一个简单的本地服务器(比如 VSCode 的 Live Server 插件)。

好了 这就是全部内容了 希望你可以在学习的道路上不断前行 加油!!!