JavaScript:ES6 + 高级语法、模块化、元编程

📅 发布时间:2026/7/31 5:11:34
JavaScript:ES6 + 高级语法、模块化、元编程
第十一阶段ES6 高级语法、模块化、元编程11.1模块化系统「ES6」ES Module 模块标准 import /export默认导出 export default、具名导出 export默认导出一个模块只能有一个默认导出使用export default关键字。例如在mathUtils.js文件中// mathUtils.jsconstadd(a,b)ab;exportdefaultadd;在另一个文件中导入// main.jsimportaddfrom./mathUtils.js;console.log(add(2,3));具名导出可以有多个具名导出使用export关键字。例如在geometry.js文件中// geometry.jsexportconstcircleArea(r)Math.PI*r*r;exportconstsquareArea(side)side*side;在另一个文件中导入// main.jsimport{circleArea,squareArea}from./geometry.js;console.log(circleArea(5));console.log(squareArea(4));导入语法import、import * as、import 解构导入普通导入如上述导入默认导出和具名导出的方式。import * as将模块所有导出内容作为一个对象导入。例如对于geometry.js文件// main.jsimport*asgeometryfrom./geometry.js;console.log(geometry.circleArea(3));console.log(geometry.squareArea(2));import 解构导入从模块中提取具名导出并赋予新的变量名。例如对于geometry.js文件// main.jsimport{circleAreaasareaOfCircle,squareAreaasareaOfSquare}from./geometry.js;console.log(areaOfCircle(4));console.log(areaOfSquare(3));动态导入 import ()「ES6」返回 Promise动态导入允许在代码执行时按需导入模块返回一个Promise。例如// 根据条件动态导入模块if(someCondition){import(./mathUtils.js).then(mathUtils{console.log(mathUtils.add(1,2));});}模块作用域、严格模式自动开启模块作用域每个 ES Module 都有自己独立的作用域模块内部定义的变量、函数等不会污染全局作用域。例如在module1.js中定义变量count// module1.jsletcount10;// 这里的 count 只在 module1.js 内有效不会影响其他模块严格模式自动开启ES Module 自动处于严格模式这意味着一些不安全或不规范的操作会报错。例如在严格模式下不能使用未声明的变量// module2.js// 以下代码会报错因为严格模式下不允许使用未声明的变量undeclaredVar10;与 CommonJSNode区别对比加载方式ES Module是静态加载在编译阶段就确定模块的依赖关系和导入导出。这使得在编译时就能进行一些优化如 tree- shaking摇树优化去除未使用的代码。CommonJS是动态加载在运行时才确定模块的依赖关系和导出。例如// CommonJS 示例constmathUtilsrequire(./mathUtils.js);// 这里 require 是在运行时执行根据运行时条件可能会加载不同的模块导出方式ES Module有默认导出和具名导出语法更灵活。CommonJS通过module.exports或exports导出例如// mathUtils.jsCommonJSfunctionadd(a,b){returnab;}module.exportsadd;// 或者exports.addadd;导入方式ES Module使用import导入。CommonJS使用require导入。例如// main.jsCommonJSconstmathUtilsrequire(./mathUtils.js);console.log(mathUtils(2,3));循环依赖处理ES Module在静态分析阶段就处理循环依赖对于已经加载但未完全求值的模块会先使用其部分值等模块完全求值后再更新。CommonJS如果遇到循环依赖在加载模块时会先返回一个未完全初始化的模块对象可能导致获取到的值不正确。例如// a.jsCommonJSconstbrequire(./b.js);console.log(a.js:,b.value);exports.valuea 的值;// b.jsCommonJSconstarequire(./a.js);console.log(b.js:,a.value);exports.valueb 的值;在上述 CommonJS 示例中由于循环依赖a.js中打印b.value可能得到undefined而 ES Module 对这种情况有更合理的处理方式。11.2 元编程拦截、代理对象「ES6」Proxy 代理对象拦截对象读写、删除、函数调用等 13 种捕获器是什么Proxy用于创建一个代理对象该对象可以拦截并自定义基本操作如属性查找、赋值、枚举、函数调用等。它接收两个参数目标对象和处理程序对象处理程序对象包含各种捕获器trap函数用于定义拦截后的行为。为什么通过Proxy我们可以在不直接修改对象本身的情况下对对象的各种操作进行控制和扩展实现诸如数据验证、日志记录、访问控制等功能。这有助于提高代码的可维护性和安全性。怎么用以下是一些常见捕获器的示例get 捕获器拦截对象属性的读取操作。例如consttarget{name:John};consthandler{get(target,property){if(propertyintarget){returntarget[property];}else{return属性${property}不存在;}}};constproxynewProxy(target,handler);console.log(proxy.name);console.log(proxy.age);set 捕获器拦截对象属性的设置操作。例如consttarget{};consthandler{set(target,property,value){if(typeofvaluenumbervalue0){thrownewError(值不能为负数);}target[property]value;returntrue;}};constproxynewProxy(target,handler);proxy.age25;// proxy.age -5; // 抛出错误apply 捕获器拦截函数调用操作。例如consttargetfunction(a,b){returnab;};consthandler{apply(target,thisArg,argumentsList){console.log(调用前日志);constresulttarget.apply(thisArg,argumentsList);console.log(调用后日志);returnresult;}};constproxynewProxy(target,handler);console.log(proxy(2,3));13 种捕获器除了上述get、set、apply外还有has拦截in操作符、deleteProperty拦截delete操作符、ownKeys拦截Object.keys()等获取对象自身属性的操作、getOwnPropertyDescriptor拦截获取属性描述符的操作、defineProperty拦截定义属性的操作、preventExtensions拦截Object.preventExtensions()操作、isExtensible拦截Object.isExtensible()操作、getPrototypeOf拦截Object.getPrototypeOf()操作、setPrototypeOf拦截Object.setPrototypeOf()操作、construct拦截new操作符。Reflect 反射 API配合 Proxy 操作对象底层行为是什么Reflect是一个内置的对象它提供了一系列静态方法这些方法与对象的底层操作相对应例如Reflect.get()、Reflect.set()等。它的方法与Object上的一些方法类似但Reflect的方法更接近对象操作的底层并且在与Proxy配合使用时能提供更一致和安全的操作方式。为什么Reflect使我们能够以更优雅和标准的方式操作对象的底层行为特别是在Proxy捕获器中使用Reflect方法可以确保遵循对象操作的默认行为同时又能进行自定义扩展。这有助于避免在操作对象时出现意外行为提高代码的健壮性。怎么用以下是与Proxy配合使用的示例consttarget{name:Alice};consthandler{get(target,property){returnReflect.get(target,property);},set(target,property,value){returnReflect.set(target,property,value);}};constproxynewProxy(target,handler);console.log(proxy.name);proxy.age30;console.log(target.age);在上述示例中Proxy的捕获器中使用Reflect的方法来执行对象的默认操作这样既实现了代理的功能又遵循了对象操作的标准行为。内置 Symbol 元符号Symbol.hasInstance是什么当其他对象使用instanceof操作符检测是否为当前对象的实例时会调用这个符号对应的方法。例如classMyClass{[Symbol.hasInstance](instance){returninstanceinstanceofArray;}}constmyClassnewMyClass();console.log([]instanceofmyClass);console.log({}instanceofmyClass);Symbol.toStringTag是什么用于定义对象在Object.prototype.toString()方法返回的字符串中的标签。例如constmyObj{[Symbol.toStringTag]:MyCustomObject};console.log(Object.prototype.toString.call(myObj));Symbol.toPrimitive是什么当对象被转换为原始值时会调用这个符号对应的方法。例如constmyNumber{value:42,[Symbol.toPrimitive](hint){if(hintnumber){returnthis.value;}elseif(hintstring){return数字:${this.value};}returnthis.value;}};console.log(myNumber);console.log(${myNumber});11.3 其他高级 ES6 语法块级作用域、顶层 await「ES6」模块顶层异步块级作用域是什么在 ES6 之前JavaScript 只有函数作用域和全局作用域。块级作用域通过let和const关键字实现以一对花括号{}为边界在块内声明的变量在块外不可访问。例如{letlocalVar10;constconstVar20;}// console.log(localVar); // 报错localVar 不在作用域内// console.log(constVar); // 报错constVar 不在作用域内为什么块级作用域可以有效避免变量提升和意外的全局变量声明使代码的逻辑更加清晰减少命名冲突。顶层 await「ES6」模块顶层异步已在异步编程顶层 await部分讲解数字分隔符 1_000_000「ES6」是什么数字分隔符_允许在数字字面量中插入下划线以提高数字的可读性特别是对于较大的数字。例如letlargeNumber1_000_000;console.log(largeNumber);这里1_000_000和1000000是等价的但前者更易读尤其是在处理财务数据或大数据量时。为什么在金融、科学计算等领域经常会遇到非常大的数字使用数字分隔符可以使数字更易读减少因看错数字位数而导致的错误。对象私有字段 #prop「ES6」是什么对象私有字段使用#前缀声明只能在对象内部访问外部无法直接访问或修改。例如classMyClass{#privateProp私有属性;getPrivateProp(){returnthis.#privateProp;}}constmyObjnewMyClass();// console.log(myObj.#privateProp); // 报错无法在外部访问console.log(myObj.getPrivateProp());为什么这有助于封装对象的内部状态保护对象的隐私和数据安全避免外部代码意外修改对象的内部数据提高代码的健壮性和可维护性。空对象字面量优化、可选链、空值合并前文运算符部分已覆盖导入断言 import assertions「ES6」是什么导入断言为import语句提供了一种验证和配置模块导入的方式。例如当导入 JSON 文件时可以使用导入断言确保导入的数据符合特定的结构。例如importdatafrom./data.jsonassert{type:json};console.log(data);这里assert { type: json }断言导入的文件是 JSON 类型。如果导入的不是 JSON 格式会抛出错误。为什么在复杂的项目中确保导入的数据或模块符合预期的格式或类型非常重要。导入断言可以在导入时就进行验证避免在后续代码执行中因数据格式错误而导致难以调试的问题提高代码的稳定性和可靠性。实战练习import () 动态按需加载组件返回 PromiseProxy 拦截对象读取、修改、删除实现数据监听Reflect 配合 Proxy 完成底层对象操作思考题ES Module 和 CommonJS 核心区别Proxy 能否代理原始基本类型数字分隔符仅语法糖运行时会自动忽略吗