怎么在新建网站上做分享代码做一个功能的页面多少钱一张

张小明 2025/12/29 16:14:56
怎么在新建网站上做分享代码,做一个功能的页面多少钱一张,下沙经济开发区建设局网站,温州做网站建设哪家好欢迎来到本次关于“装饰器模式”的专题讲座。今天#xff0c;我们将深入探讨装饰器模式在现代JavaScript开发中的应用#xff0c;特别是如何利用高阶函数#xff08;Higher-Order Functions, HOFs#xff09;和ES6 Proxy这两种强大的语言特性来动态增强对象功能。装饰器模式…欢迎来到本次关于“装饰器模式”的专题讲座。今天我们将深入探讨装饰器模式在现代JavaScript开发中的应用特别是如何利用高阶函数Higher-Order Functions, HOFs和ES6 Proxy这两种强大的语言特性来动态增强对象功能。装饰器模式是一种结构型设计模式它允许在不修改原有对象结构的情况下向对象添加新的行为或责任。这种模式的核心思想是“包装”一个装饰器将一个对象包裹起来并在其上添加额外的功能同时保持原有的接口不变。这种方式提供了一种比继承更灵活的替代方案因为它可以在运行时动态地组合功能避免了继承体系的僵化和“类爆炸”的问题。1. 装饰器模式的本质功能增强与职责分离在软件设计中我们经常面临这样的需求一个对象需要具备多种可选的功能或者在特定条件下需要改变其行为。如果采用传统的继承方式可能会导致以下问题类爆炸Class Explosion为每个功能组合创建新的子类导致类的数量急剧增加维护困难。僵硬的继承链一旦确定了继承关系就很难在运行时改变对象的行为。违背单一职责原则基类可能会因为需要支持多种扩展而变得臃肿承担了过多的责任。装饰器模式正是为了解决这些问题而生。它的核心思想可以概括为开放/封闭原则Open/Closed Principle软件实体类、模块、函数等应该对扩展开放对修改封闭。这意味着我们应该在不修改现有代码的基础上添加新功能。动态组合功能可以在运行时根据需要动态地添加到对象上而不是在编译时通过继承固定。透明性装饰后的对象与原始对象拥有相同的接口使用者无需关心是否被装饰。我们来看一个经典的比喻咖啡店的咖啡。一杯基础咖啡Concrete Component可以加上牛奶Concrete Decorator、糖Concrete Decorator或其他配料。每加一种配料咖啡的成本和描述都会改变但它仍然是“咖啡”。// 1. 抽象组件 (Component) - 定义了被装饰对象和装饰器共同的接口 class Coffee { cost() { throw new Error(Abstract method cost must be implemented.); } description() { throw new Error(Abstract method description must be implemented.); } } // 2. 具体组件 (Concrete Component) - 原始对象 class SimpleCoffee extends Coffee { cost() { return 5; } description() { return Simple Coffee; } } // 3. 抽象装饰器 (Decorator) - 维持一个指向组件对象的引用并实现组件接口 class CoffeeDecorator extends Coffee { constructor(coffee) { super(); this.decoratedCoffee coffee; } cost() { return this.decoratedCoffee.cost(); // 默认行为是委托给被装饰对象 } description() { return this.decoratedCoffee.description(); // 默认行为是委托给被装饰对象 } } // 4. 具体装饰器 (Concrete Decorator) - 向组件添加额外的职责 class MilkDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); } cost() { return super.cost() 2; // 添加牛奶的成本 } description() { return super.description() , with Milk; // 添加牛奶的描述 } } class SugarDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); } cost() { return super.cost() 1; // 添加糖的成本 } description() { return super.description() , with Sugar; // 添加糖的描述 } } class CaramelDecorator extends CoffeeDecorator { constructor(coffee) { super(coffee); } cost() { return super.cost() 3; } description() { return super.description() , with Caramel; } } // 使用示例 let myCoffee new SimpleCoffee(); console.log(Cost: ${myCoffee.cost()}, Description: ${myCoffee.description()}); // 输出: Cost: 5, Description: Simple Coffee myCoffee new MilkDecorator(myCoffee); console.log(Cost: ${myCoffee.cost()}, Description: ${myCoffee.description()}); // 输出: Cost: 7, Description: Simple Coffee, with Milk myCoffee new SugarDecorator(myCoffee); console.log(Cost: ${myCoffee.cost()}, Description: ${myCoffee.description()}); // 输出: Cost: 8, Description: Simple Coffee, with Milk, with Sugar myCoffee new CaramelDecorator(myCoffee); console.log(Cost: ${myCoffee.cost()}, Description: ${myCoffee.description()}); // 输出: Cost: 11, Description: Simple Coffee, with Milk, with Sugar, with Caramel // 也可以直接创建一个带牛奶和焦糖的咖啡 let fancyCoffee new CaramelDecorator(new MilkDecorator(new SimpleCoffee())); console.log(Cost: ${fancyCoffee.cost()}, Description: ${fancyCoffee.description()}); // 输出: Cost: 10, Description: Simple Coffee, with Milk, with Caramel上述代码展示了传统面向对象语言中装饰器模式的实现方式。它依赖于类和继承来构建装饰器链。然而在JavaScript这个多范式语言中特别是ES6及以后我们拥有更灵活、更函数式的实现方式。2. 利用高阶函数Higher-Order Functions实现函数装饰器在JavaScript中函数是“一等公民”。这意味着函数可以像其他任何值一样被赋值给变量、作为参数传递、或者作为另一个函数的返回值。这种特性为实现函数装饰器提供了天然的土壤我们称之为高阶函数HOF。高阶函数是指接受一个或多个函数作为参数。返回一个函数。当一个高阶函数接受一个函数作为参数并返回一个增强了其功能的新函数时它就扮演了“函数装饰器”的角色。这种模式在函数式编程中非常常见。2.1 基础函数装饰器示例日志记录假设我们有一个重要的函数我们希望在每次调用它时都能记录其输入参数和返回值而无需修改函数本身的逻辑。/** * 日志记录装饰器 * param {Function} func 要装饰的函数 * returns {Function} 装饰后的函数 */ function logDecorator(func) { return function(...args) { console.log([LOG] Calling function: ${func.name || anonymous}); console.log([LOG] Arguments: ${JSON.stringify(args)}); try { const result func(...args); console.log([LOG] Function ${func.name || anonymous} returned: ${JSON.stringify(result)}); return result; } catch (error) { console.error([LOG] Function ${func.name || anonymous} threw an error: ${error.message}); throw error; // 重新抛出错误 } }; } // 原始函数 function add(a, b) { return a b; } function multiply(a, b) { return a * b; } // 装饰函数 const loggedAdd logDecorator(add); const loggedMultiply logDecorator(multiply); // 调用装饰后的函数 console.log(--- Calling loggedAdd ---); console.log(Result: ${loggedAdd(2, 3)}); // 预期输出: // [LOG] Calling function: add // [LOG] Arguments: [2,3] // [LOG] Function add returned: 5 // Result: 5 console.log(n--- Calling loggedMultiply ---); console.log(Result: ${loggedMultiply(4, 5)}); // 预期输出: // [LOG] Calling function: multiply // [LOG] Arguments: [4,5] // [LOG] Function multiply returned: 20 // Result: 20 // 验证错误处理 function divide(a, b) { if (b 0) { throw new Error(Cannot divide by zero.); } return a / b; } const loggedDivide logDecorator(divide); console.log(n--- Calling loggedDivide with error ---); try { loggedDivide(10, 0); } catch (e) { console.error(Caught error outside decorator: ${e.message}); } // 预期输出: // [LOG] Calling function: divide // [LOG] Arguments: [10,0] // [LOG] Function divide threw an error: Cannot divide by zero. // Caught error outside decorator: Cannot divide by zero.2.2 性能测量装饰器另一个常见的需求是测量函数执行所需的时间。/** * 性能测量装饰器 * param {Function} func 要装饰的函数 * returns {Function} 装饰后的函数 */ function timingDecorator(func) { return function(...args) { const start performance.now(); // 记录开始时间 try { const result func(...args); const end performance.now(); // 记录结束时间 console.log([TIME] Function ${func.name || anonymous} executed in ${end - start} ms.); return result; } catch (error) { const end performance.now(); console.error([TIME] Function ${func.name || anonymous} failed after ${end - start} ms with error: ${error.message}); throw error; } }; } // 原始函数 function calculateExpensiveResult(n) { let sum 0; for (let i 0; i n; i) { sum Math.sqrt(i) * Math.log(i 1); } return sum; } // 装饰函数 const timedCalculate timingDecorator(calculateExpensiveResult); // 调用装饰后的函数 console.log(n--- Calling timedCalculate ---); console.log(Result: ${timedCalculate(1000000)}); // 预期输出: // [TIME] Function calculateExpensiveResult executed in X ms. // Result: Y2.3 防抖Debounce装饰器防抖是一种优化技术用于限制函数在一定时间段内被调用的频率。当事件被触发时函数不会立即执行而是等待一段时间。如果在等待期间事件再次被触发则重新开始计时。/** * 防抖装饰器 * param {Function} func 要装饰的函数 * param {number} delay 延迟时间毫秒 * returns {Function} 装饰后的函数 */ function debounce(func, delay) { let timeoutId; return function(...args) { const context this; // 保存函数执行时的上下文 clearTimeout(timeoutId); // 清除之前的定时器 timeoutId setTimeout(() { func.apply(context, args); // 在延迟后执行原始函数 }, delay); }; } // 原始函数 function searchInput(query) { console.log(Searching for: ${query}); } // 装饰函数 const debouncedSearch debounce(searchInput, 500); // 模拟用户输入 console.log(n--- Calling debouncedSearch ---); debouncedSearch(apple); // 0ms debouncedSearch(app); // 100ms debouncedSearch(appl); // 200ms debouncedSearch(apple); // 300ms // 预期输出: 只有最后一次调用在500ms后执行 // Searching for: apple (在约 800ms 时)2.4 节流Throttle装饰器节流是另一种优化技术它确保函数在指定时间间隔内只执行一次。无论事件触发频率多高函数都会以固定的速率执行。/** * 节流装饰器 * param {Function} func 要装饰的函数 * param {number} interval 间隔时间毫秒 * returns {Function} 装饰后的函数 */ function throttle(func, interval) { let timeoutId; let lastArgs; let lastContext; let lastExecutionTime 0; return function(...args) { lastArgs args; lastContext this; const now Date.now(); if (now - lastExecutionTime interval) { // 如果距离上次执行时间超过间隔则立即执行 func.apply(lastContext, lastArgs); lastExecutionTime now; } else { // 否则设置一个定时器在间隔结束后执行 clearTimeout(timeoutId); timeoutId setTimeout(() { if (Date.now() - lastExecutionTime interval) { func.apply(lastContext, lastArgs); lastExecutionTime Date.now(); } }, interval - (now - lastExecutionTime)); } }; } // 原始函数 function handleScroll(event) { console.log(Scroll event at ${Date.now() % 10000}: ${event}); } // 装饰函数 const throttledScroll throttle(handleScroll, 1000); // 每秒最多执行一次 // 模拟频繁滚动 console.log(n--- Calling throttledScroll ---); // 假设这些调用发生在很短的时间内 for (let i 0; i 10; i) { setTimeout(() throttledScroll(Scroll ${i 1}), i * 100); } // 预期输出: // Scroll event at X: Scroll 1 (立即执行) // Scroll event at Y: Scroll N (在约 1000ms 后执行一次) // ... (每秒执行一次直到所有待处理的调用都执行完毕)2.5 记忆化Memoization装饰器记忆化是一种优化技术用于缓存函数的计算结果。如果函数在相同的输入下被多次调用它会直接返回之前缓存的结果而不是重新计算。/** * 记忆化装饰器 * param {Function} func 要装饰的函数 * returns {Function} 装饰后的函数 */ function memoize(func) { const cache new Map(); // 使用Map来存储缓存结果 return function(...args) { const key JSON.stringify(args); // 将参数序列化为缓存键 if (cache.has(key)) { console.log([MEMO] Returning cached result for ${func.name || anonymous}(${key})); return cache.get(key); } console.log([MEMO] Calculating result for ${func.name || anonymous}(${key})); const result func(...args); cache.set(key, result); // 缓存结果 return result; }; } // 原始函数 (模拟耗时计算) function fibonacci(n) { if (n 1) return n; return fibonacci(n - 1) fibonacci(n - 2); } // 装饰函数 const memoizedFibonacci memoize(fibonacci); // 调用装饰后的函数 console.log(n--- Calling memoizedFibonacci ---); console.log(Fib(5): ${memoizedFibonacci(5)}); console.log(Fib(8): ${memoizedFibonacci(8)}); console.log(Fib(5): ${memoizedFibonacci(5)}); // 第二次调用应该从缓存中获取 console.log(Fib(3): ${memoizedFibonacci(3)}); console.log(Fib(8): ${memoizedFibonacci(8)}); // 第二次调用应该从缓存中获取 // 预期输出: // [MEMO] Calculating result for fibonacci([5]) // ... (一系列递归计算和缓存) // Fib(5): 5 // [MEMO] Calculating result for fibonacci([8]) // ... // Fib(8): 21 // [MEMO] Returning cached result for fibonacci([5]) // Fib(5): 5 // [MEMO] Calculating result for fibonacci([3]) // ... // Fib(3): 2 // [MEMO] Returning cached result for fibonacci([8]) // Fib(8): 212.6 HOF装饰器的优势与局限性优势:简洁与函数式风格利用JavaScript函数作为一等公民的特性实现方式非常简洁符合函数式编程范式。高复用性装饰器本身是纯函数可以方便地应用于任何兼容的函数。可组合性多个HFO装饰器可以像管道一样串联起来形成更复杂的功能。无副作用通常不会修改原始函数而是返回一个新函数符合不可变性原则。局限性:仅限于函数HFO装饰器主要用于增强函数的功能。它们不能直接用于装饰对象的属性访问、设置或实例化等行为。上下文丢失问题在某些情况下如果装饰器内部不正确地处理this上下文例如在返回的匿名函数中使用箭头函数或显式bind/apply/call可能会导致原始函数的this指向不正确。3. 利用ES6 Proxy动态增强对象功能高阶函数在装饰函数方面表现出色但当我们需要对一个对象的多种操作如属性访问、属性设置、方法调用、删除属性等进行拦截和增强时高阶函数就显得力不从心了。这时ES6引入的Proxy代理对象就成为了理想的解决方案。Proxy对象用于创建一个对象的代理从而允许你拦截并自定义对该对象的各种基本操作例如属性查找、赋值、枚举、函数调用等。它提供了一种在不修改目标对象本身的情况下对其行为进行“元编程”的能力。一个Proxy对象由两个主要部分组成target(目标对象)被代理的实际对象。handler(处理程序对象)一个包含各种“陷阱”trap方法的对象这些方法定义了在对target进行操作时要执行的自定义行为。3.1 基础Proxy装饰器示例属性访问日志我们可以用Proxy来记录每次对对象属性的访问。/** * 属性访问日志装饰器 * param {Object} target 要装饰的目标对象 * returns {Proxy} 装饰后的代理对象 */ function createPropertyAccessLogger(target) { return new Proxy(target, { get(obj, prop, receiver) { console.log([PROXY-LOG] Accessing property: ${String(prop)}); // 默认行为返回属性值 return Reflect.get(obj, prop, receiver); }, set(obj, prop, value, receiver) { console.log([PROXY-LOG] Setting property: ${String(prop)} to value: ${JSON.stringify(value)}); // 默认行为设置属性值 return Reflect.set(obj, prop, value, receiver); }, apply(obj, thisArg, argumentsList) { // 如果目标是一个函数则拦截其调用 console.log([PROXY-LOG] Calling method: ${obj.name || anonymous} with args: ${JSON.stringify(argumentsList)}); return Reflect.apply(obj, thisArg, argumentsList); } }); } // 原始对象 const user { name: Alice, age: 30, greet() { return Hello, my name is ${this.name}.; } }; // 装饰对象 const loggedUser createPropertyAccessLogger(user); // 访问属性 console.log(n--- Accessing loggedUser properties ---); console.log(User name: ${loggedUser.name}); // 触发 get trap loggedUser.age 31; // 触发 set trap console.log(User age: ${loggedUser.age}); // 触发 get trap // 调用方法 console.log(Greeting: ${loggedUser.greet()}); // 触发 get trap (获取 greet 方法)然后触发 apply trap (调用 greet 方法) // 添加新属性 loggedUser.city New York; // 触发 set trap console.log(User city: ${loggedUser.city});3.2 验证装饰器Proxy可以用来实现强大的验证逻辑确保对象属性始终处于有效状态。/** * 验证装饰器 * param {Object} target 要装饰的目标对象 * param {Object} schema 验证规则 * returns {Proxy} 装饰后的代理对象 */ function createValidator(target, schema) { return new Proxy(target, { set(obj, prop, value, receiver) { if (schema[prop]) { const validationRule schema[prop]; if (validationRule.type typeof value ! validationRule.type) { throw new TypeError(Property ${String(prop)} must be of type ${validationRule.type}. Received ${typeof value}.); } if (validationRule.min ! undefined value validationRule.min) { throw new RangeError(Property ${String(prop)} must be at least ${validationRule.min}.); } if (validationRule.max ! undefined value validationRule.max) { throw new RangeError(Property ${String(prop)} must be at most ${validationRule.max}.); } if (validationRule.validate !validationRule.validate(value)) { throw new Error(Validation failed for property ${String(prop)}.); } } return Reflect.set(obj, prop, value, receiver); } }); } // 原始对象 const config { port: 8080, timeout: 5000, env: development }; // 验证规则 const configSchema { port: { type: number, min: 1024, max: 65535 }, timeout: { type: number, min: 1000 }, env: { type: string, validate: (val) [development, production, test].includes(val) } }; // 装饰对象 const validatedConfig createValidator(config, configSchema); console.log(n--- Testing validatedConfig ---); try { validatedConfig.port 3000; // 有效 console.log(Port updated to: ${validatedConfig.port}); // validatedConfig.port 80; // 无效抛出 RangeError // console.log(Port updated to: ${validatedConfig.port}); // validatedConfig.timeout 500; // 无效抛出 RangeError // console.log(Timeout updated to: ${validatedConfig.timeout}); validatedConfig.env production; // 有效 console.log(Environment updated to: ${validatedConfig.env}); // validatedConfig.env staging; // 无效抛出 Error // console.log(Environment updated to: ${validatedConfig.env}); validatedConfig.port invalid; // 无效抛出 TypeError console.log(Port updated to: ${validatedConfig.port}); } catch (e) { console.error(Validation Error: ${e.message}); }3.3 访问控制/权限管理装饰器Proxy可以用来实现细粒度的访问控制根据用户角色或其他条件限制对对象属性或方法的访问。/** * 访问控制装饰器 * param {Object} target 要装饰的目标对象 * param {Object} permissions 权限映射 { propertyName: [role1, role2] } * param {string} currentUserRole 当前用户角色 * returns {Proxy} 装饰后的代理对象 */ function createAccessController(target, permissions, currentUserRole) { return new Proxy(target, { get(obj, prop, receiver) { if (permissions[prop] !permissions[prop].includes(currentUserRole)) { throw new Error(Access Denied: User ${currentUserRole} cannot read property ${String(prop)}.); } return Reflect.get(obj, prop, receiver); }, set(obj, prop, value, receiver) { // 假设写入权限和读取权限一致或者可以定义独立的写入权限 if (permissions[prop] !permissions[prop].includes(currentUserRole)) { throw new Error(Access Denied: User ${currentUserRole} cannot write property ${String(prop)}.); } return Reflect.set(obj, prop, value, receiver); }, deleteProperty(obj, prop) { if (permissions[prop] !permissions[prop].includes(currentUserRole)) { throw new Error(Access Denied: User ${currentUserRole} cannot delete property ${String(prop)}.); } return Reflect.deleteProperty(obj, prop); } }); } // 原始对象 const secretData { personalInfo: SSN: XXX-XX-XXXX, salary: 100000, department: Engineering, projectName: Project Alpha }; // 权限规则 const dataPermissions { personalInfo: [admin], salary: [admin, manager], department: [admin, manager, employee], projectName: [admin, manager, employee] }; console.log(n--- Testing Access Control ---); // 以普通员工身份访问 try { const employeeData createAccessController(secretData, dataPermissions, employee); console.log(Employee can see department: ${employeeData.department}); console.log(Employee can see project name: ${employeeData.projectName}); // console.log(Employee tries to see salary: ${employeeData.salary}); // 抛出错误 } catch (e) { console.error(Employee Access Error: ${e.message}); } // 以经理身份访问 try { const managerData createAccessController(secretData, dataPermissions, manager); console.log(Manager can see department: ${managerData.department}); console.log(Manager can see salary: ${managerData.salary}); // managerData.salary 120000; // 假设允许经理修改工资 // console.log(Manager updated salary to: ${managerData.salary}); // console.log(Manager tries to see personal info: ${managerData.personalInfo}); // 抛出错误 } catch (e) { console.error(Manager Access Error: ${e.message}); } // 以管理员身份访问 try { const adminData createAccessController(secretData, dataPermissions, admin); console.log(Admin can see personal info: ${adminData.personalInfo}); console.log(Admin can see salary: ${adminData.salary}); adminData.salary 150000; console.log(Admin updated salary to: ${adminData.salary}); delete adminData.personalInfo; // 假设允许管理员删除 console.log(Admin deleted personalInfo.); console.log(Admin tries to see personal info after deletion: ${adminData.personalInfo}); // undefined } catch (e) { console.error(Admin Access Error: ${e.message}); }3.4 只读Read-Only装饰器Proxy可以轻松地将一个对象转换为只读状态防止对其属性进行修改或删除。/** * 只读装饰器 * param {Object} target 要装饰的目标对象 * returns {Proxy} 装饰后的代理对象 */ function createReadOnlyProxy(target) { return new Proxy(target, { set(obj, prop, value) { throw new Error(Cannot set property ${String(prop)}: Object is read-only.); }, deleteProperty(obj, prop) { throw new Error(Cannot delete property ${String(prop)}: Object is read-only.); }, // 阻止扩展属性 preventExtensions(obj) { return false; // 总是返回false表示不能阻止扩展 }, // 阻止配置属性 defineProperty(obj, prop, descriptor) { throw new Error(Cannot define property ${String(prop)}: Object is read-only.); } }); } // 原始对象 const immutableSettings { API_KEY: abc123xyz, DEBUG_MODE: false, VERSION: 1.0.0 }; // 装饰对象 const readOnlySettings createReadOnlyProxy(immutableSettings); console.log(n--- Testing Read-Only Proxy ---); console.log(API Key: ${readOnlySettings.API_KEY}); console.log(Debug Mode: ${readOnlySettings.DEBUG_MODE}); try { readOnlySettings.DEBUG_MODE true; // 尝试修改抛出错误 } catch (e) { console.error(Read-Only Error: ${e.message}); } try { readOnlySettings.NEW_PROP value; // 尝试添加新属性抛出错误 } catch (e) { console.error(Read-Only Error: ${e.message}); } try { delete readOnlySettings.VERSION; // 尝试删除属性抛出错误 } catch (e) { console.error(Read-Only Error: ${e.message}); }3.5 Proxy装饰器的优势与局限性优势:全面拦截Proxy能够拦截几乎所有对目标对象的基本操作包括属性的读取、设置、删除方法的调用迭代甚至对象自身的反射操作如Object.keys。细粒度控制可以针对不同的属性或方法定义不同的拦截逻辑。真正的对象装饰能够对整个对象进行功能增强而不仅仅是函数。元编程能力提供了一种强大的元编程能力可以在运行时改变对象的底层行为。局限性:性能开销与直接操作原始对象相比Proxy会有一定的性能开销。虽然现代JavaScript引擎已经对此进行了优化但在对性能要求极高的场景中仍需谨慎。调试复杂性当一个对象被多层Proxy包裹时调试起来可能会比较复杂因为调用栈会变得更深且真实的错误源可能被隐藏在代理层之后。this上下文问题在使用Proxy代理方法时需要确保this上下文正确地指向原始对象通常通过Reflect.apply或Function.prototype.apply来处理。兼容性ES6 Proxy在旧版浏览器中可能不被支持尽管现在主流浏览器支持度已很高。3.6 HOFs与Proxies的比较特性高阶函数 (HOFs)ES6 Proxy目标增强函数的功能增强对象的各种操作属性、方法、原型等拦截范围仅限于函数调用几乎所有对象操作get, set, apply, delete等实现方式返回一个新函数在新函数中调用原函数返回一个代理对象通过陷阱方法拦截操作灵活性在函数层面非常灵活和可组合在对象层面非常灵活可以实现细粒度控制性能通常开销较小存在一定开销但通常可接受调试相对直接调用栈清晰可能增加调试复杂性调用栈更深应用场景日志、计时、缓存、防抖、节流、权限校验等函数操作验证、访问控制、ORM、数据绑定、对象虚拟化等组合方式函数链式调用decorator1(decorator2(func))嵌套代理new Proxy(new Proxy(target, h1), h2)4. 结合 HOFs 和 Proxies更强大的装饰器模式在实际开发中我们可能会遇到需要同时装饰对象的方法和属性的情况。这时我们可以将高阶函数和Proxy结合起来使用。例如我们可以创建一个Proxy来拦截对象的方法调用并在Proxy的apply陷阱中使用高阶函数来装饰被调用的方法。// 重新定义一个通用的日志记录装饰器用于函数 function methodLogger(func) { if (typeof func ! function) { return func; // 如果不是函数直接返回不做处理 } return function(...args) { console.log([Method-LOG] Calling method: ${func.name || anonymous} with args: ${JSON.stringify(args)}); const result func.apply(this, args); // 确保正确的this上下文 console.log([Method-LOG] Method ${func.name || anonymous} returned: ${JSON.stringify(result)}); return result; }; } /** * 混合装饰器使用Proxy拦截对象操作并使用HOF装饰其中的方法 * param {Object} target 目标对象 * param {Function[]} methodDecorators 应用于方法的HOF装饰器数组 * returns {Proxy} 装饰后的代理对象 */ function createHybridDecorator(target, methodDecorators []) { // 创建一个临时的对象来存储装饰后的方法避免直接修改原始target const decoratedMethods {}; for (const key in target) { if (typeof target[key] function) { let decoratedFunc target[key]; for (const decorator of methodDecorators) { decoratedFunc decorator(decoratedFunc); } decoratedMethods[key] decoratedFunc; } } return new Proxy(target, { get(obj, prop, receiver) { // 如果是方法返回预先装饰好的方法 if (typeof obj[prop] function decoratedMethods[prop]) { // 绑定this上下文到原始对象确保方法内部的this指向正确 return decoratedMethods[prop].bind(receiver); } // 否则执行默认的属性获取行为 return Reflect.get(obj, prop, receiver); }, set(obj, prop, value, receiver) { console.log([Hybrid-PROXY] Setting property: ${String(prop)} to value: ${JSON.stringify(value)}); return Reflect.set(obj, prop, value, receiver); }, // 也可以在这里添加其他Proxy陷阱来装饰非方法属性的行为 // 例如可以像之前的验证装饰器一样在这里对属性设置进行验证 }); } // 原始对象 const service { data: initial data, process(input) { console.log(Processing input: ${input}); return Processed: ${input} - ${this.data}; }, getData() { return this.data; }, // 一个非方法属性 config: { version: 1 } }; // 使用混合装饰器为所有方法添加日志 const decoratedService createHybridDecorator(service, [methodLogger, timingDecorator]); console.log(n--- Testing Hybrid Decorator ---); console.log(Result from process: ${decoratedService.process(test message)}); console.log(Result from getData: ${decoratedService.getData()}); decoratedService.data new data; // 触发Proxy的set陷阱 console.log(Updated data: ${decoratedService.data}); // 触发Proxy的get陷阱 decoratedService.config.version 2; // 注意这里只会触发对config属性的get不会拦截config内部属性的修改 console.log(Config version: ${decoratedService.config.version});这个例子展示了如何通过Proxy的get陷阱来拦截方法的访问并返回一个已经被高阶函数装饰过的新方法。这样我们既能利用Proxy对整个对象操作的全面控制又能利用HOFs对特定函数行为的精细增强。5. 装饰器在现代 JavaScript 生态中的体现装饰器模式不仅停留在理论层面它在现代JavaScript框架和库中有着广泛的应用和各种变体。TypeScript/Babel 装饰器Stage 2/3 Proposal这是一种语法糖允许你以decoratorName的形式直接在类、方法、属性或参数上应用装饰器。它们在编译时通过Babel或TypeScript被转换为高阶函数或Proxy类似的逻辑。// 假设 logMethod 和 measure 是预定义的装饰器 class MyClass { logMethod measure myMethod(arg1: string, arg2: number) { console.log(Executing myMethod with ${arg1}, ${arg2}); return arg1 arg2; } }这种语法糖极大地提升了装饰器模式的可读性和易用性使得开发者可以声明式地增强类成员的功能。React 高阶组件 (Higher-Order Components, HOCs)HOC是React中重用组件逻辑的一种高级技术。它是一个函数接受一个组件作为参数并返回一个增强了功能的新组件。这本质上就是函数装饰器模式在React组件层面的应用。function withLogging(WrappedComponent) { return class extends React.Component { componentDidMount() { console.log(Component ${WrappedComponent.name} mounted.); } render() { return WrappedComponent {...this.props} /; } }; } class MyComponent extends React.Component { /* ... */ } const LoggedMyComponent withLogging(MyComponent); // 或者使用ES7装饰器语法 withLoggingVue Mixins / Composition APIVue的混入Mixins和组合式APIComposition API也提供了类似装饰器模式的功能用于在组件之间共享和重用逻辑。虽然不是严格意义上的装饰器但它们都致力于在不修改原始代码的情况下增强组件功能。NestJS 装饰器NestJS 是一个基于TypeScript的Node.js框架它大量使用了ECMAScript装饰器来实现依赖注入、路由、守卫、管道等核心功能。例如Controller(cats) export class CatsController { constructor(private catsService: CatsService) {} Get() findAll(): string { return this.catsService.findAll(); } Post() UseGuards(AuthGuard) // 路由守卫装饰器 create(Body() createCatDto: CreateCatDto): string { return This action adds a new cat; } }这些装饰器在运行时被NestJS框架解析并执行相应的逻辑如注册路由、检查权限等。6. 最佳实践与注意事项虽然装饰器模式非常强大但在使用时也需要注意一些最佳实践和潜在问题保持装饰器的单一职责一个装饰器应该只负责添加一项特定的功能。如果一个装饰器变得过于复杂它可能包含了多个职责这会降低其复用性。链式调用与顺序多个装饰器可以链式调用。重要的是要理解它们的执行顺序。通常最内层的装饰器最先执行其增强逻辑而最外层的装饰器最后执行。避免过度装饰过度使用装饰器特别是多层嵌套的Proxy可能会使代码难以理解和调试。在选择是否使用装饰器时要权衡其带来的灵活性和可能增加的复杂性。this上下文的处理在使用高阶函数装饰类方法或Proxy拦截方法调用时务必确保this上下文被正确地保留并传递给原始函数或方法。Function.prototype.apply、Function.prototype.call或Reflect.apply是处理此问题的常用方法。性能考量虽然现代JavaScript引擎对Proxy进行了大量优化但在极端性能敏感的场景下仍需注意其可能带来的额外开销。对于高频调用的函数HOF通常比Proxy更轻量。可测试性装饰器应该易于测试。通常你可以独立测试装饰器本身然后测试被装饰的原始对象最后测试装饰器与原始对象组合后的行为。错误处理装饰器应该能够优雅地处理原始函数或对象操作可能抛出的错误并决定是重新抛出、捕获处理还是转换错误。TypeScript/ES 装饰器提案的稳定性虽然语法糖在Babel和TypeScript中广泛使用但它在ECMAScript标准中仍处于提案阶段Stage 2/3这意味着其规范可能仍然会发生变化。在生产环境中使用时应考虑其稳定性。装饰器模式无论是通过高阶函数还是ES6 Proxy实现都为JavaScript开发者提供了一种强大而灵活的工具用于在不修改核心逻辑的情况下动态地增强对象和函数的功能。它们促进了代码的模块化、可重用性和遵循开放/封闭原则是现代JavaScript开发中不可或缺的设计模式。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

光华路网站建设新蔡县做网站收多少钱

如何快速使用NBTExplorer:我的世界数据编辑终极指南 【免费下载链接】NBTExplorer A graphical NBT editor for all Minecraft NBT data sources 项目地址: https://gitcode.com/gh_mirrors/nb/NBTExplorer 想要彻底掌控你的我的世界游戏体验吗?N…

张小明 2025/12/27 19:41:37 网站建设

保定网站设计制作公司短网址生成 免费

目录 一句话总览(先给结论) 先打一个最直观的比喻(重点) 🎧 把“声卡”想成一个 超大的音箱肚子 一、旧代码到底错在哪?(超白话) 你以前是怎么“算时间”的? 用生活…

张小明 2025/12/26 8:56:11 网站建设

做印章网站wordpress的cms插件

Git钩子与项目合并策略详解 1. Git钩子概述 Git钩子是在Git执行特定操作时自动触发的脚本,可帮助开发者自动化工作流程、实施代码规范等。以下是一些常见的Git钩子及其作用: - applypatch相关钩子 : - pre - applypatch :名字虽叫“预应用补丁”,但实际在补丁应用…

张小明 2025/12/26 8:55:03 网站建设

网站建设杭州最便宜网络营销方案策划案例

为什么DrissionPage是处理网页弹窗的终极解决方案 【免费下载链接】DrissionPage 基于python的网页自动化工具。既能控制浏览器,也能收发数据包。可兼顾浏览器自动化的便利性和requests的高效率。功能强大,内置无数人性化设计和便捷功能。语法简洁而优雅…

张小明 2025/12/26 8:54:28 网站建设

做斗图的网站升级wordpress5

Mono开发与Linux安全防护全解析 1. Mono库的使用 Ubuntu系统预装了一些基于Mono构建的程序,如Tomboy和Beagle,同时还附带了许多支持.NET的库。Mono的优势在于能让开发者轻松利用这些库进行开发,只需使用 using 语句导入即可开始编程。下面将通过两个实例展示如何构建更复…

张小明 2025/12/26 8:53:52 网站建设

网站基本建设是什么网站备案价格

如何选对LED灯珠?从芯片到应用的深度实战指南你有没有遇到过这样的情况:同样的电路设计,换了个LED品牌,灯光颜色却差了一大截?或者明明标称寿命5万小时,用了不到一年就明显变暗?在照明和显示系统…

张小明 2025/12/27 21:04:57 网站建设