百度搜索公司网站展现图片,网站宣传推广文案,多元网络兰州网站建设,网站网络广告推广在JavaScript中#xff0c;闭包和代理是两种重要的概念#xff0c;它们各自具有独特的功能和用途。闭包#xff0c;它指的是一个函数能够访问并操作其父函数作用域中的变量#xff0c;即使父函数已经执行完毕。它允许内部函数访问外部函数的变量#xff0c;从而提供了强大…在JavaScript中闭包和代理是两种重要的概念它们各自具有独特的功能和用途。闭包它指的是一个函数能够访问并操作其父函数作用域中的变量即使父函数已经执行完毕。它允许内部函数访问外部函数的变量从而提供了强大的功能。而代理Proxy则是另一种高级功能它允许开发者定义基本操作的自定义行为如属性查找、赋值、枚举、函数调用等。
一、闭包
1、定义
闭包是一个函数及其相关的引用环境的组合。在JavaScript中当一个函数在另一个函数的内部定义并且这个内部函数引用了外部函数的变量时就形成了一个闭包。即使外部函数已经执行完毕内部函数仍然可以访问外部函数的变量。
2、工作原理
2.1、词法作用域
词法作用域也称为静态作用域JavaScript采用这种方式就意味着函数的作用域在函数定义时就已经确定而不是在函数执行时确定。
当一个函数在另一个函数内部定义时内部函数会捕获外部函数的词法作用域包括外部函数的参数和变量。这样就实现访问外部函数变量或参数的目的。
2.2、执行上下文和作用于链
当一个函数被调用时JavaScript引擎会为其创建一个执行上下文并构建作用域链。作用域链是一个对象列表用于解析函数中的变量。而内部函数的作用域链包含了外部函数的作用域因此内部函数可以访问外部函数的变量。
2.3、闭包的持久化
由于内部函数持有了对外部函数作用域的引用即使外部函数执行完毕这些作用域也不会被销毁。只要内部函数存在外部函数的变量就会保留在内存中从而实现“持久化”。
3、闭包使用实例
function createCounter(){let count 0;return function() {count;return count;};
}let counter createCounter();
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2
上述示例代码是在本专栏第一张中的闭包函数示例当时主要介绍函数并没有深入分析他的原理。现在我们理解了闭包的定义相信你能很轻松的解释其中原理
createCounter是一个外部函数它定义了一个局部变量count随后内部返回一个匿名函数在这个函数中访问并更改了外部函数的变量count随后将其作为返回值返回给用户。当createCounter()执行后就会将其赋值给counter由于 createCounter的返回值是一个函数所以counter也是一个函数而且该函数的作用于在createCounter里面所以每次调用counter时createCounter里面的变量count都会被修改这里是递增。
4、闭包的应用场景
4.1、数据的封装和隐藏
function Counter() {var count 0; // 私有变量function increment() {count;console.log(count);}return {increase: increment, // 返回的对象通过increment方法访问count形成闭包};
}var counter new Counter();
counter.increase(); // 输出: 1
闭包可以用来模拟类的私有属性和方法。通过在构造函数中定义并返回一个对象该对象的方法利用闭包访问构造函数内部的私有变量。
4.2、回调函数和事件处理器
JS中回调函数和事件处理器经常需要访问外部函数的变量。闭包使得这些函数即使在异步执行时也能访问外部变量。例如new XMLHttpRequest 对象中有一个事件函数onreadystatechange函数在使用它时就有可能利用到闭包
var xhr new XMLHttpRequest();
var count 0;xhr.open(GET, https://xxx.xxx.com/api, true);xhr.onreadystatechange function() {if (xhr.readyState 4 xhr.status 200) {count; // 访问外部变量var innerVariable Response received;console.log(innerVariable);}
};xhr.send();
再如在数组的forEach、map等高级函数中利用闭包
const numbers [1, 2, 3, 4, 5];function createCounter() {let count 0; // 私有变量return function() {count;return count;};
}const counter createCounter();numbers.forEach(function(number) {console.log(Number: ${number}, Count: ${counter()});
});
4.3、数据缓存
利用闭包存储先前计算的结果避免重复计算提高性能。
function expensiveCalculation(value) {let cache {};return function cachedCalculation(newValue) {if (newValue in cache) {return cache[newValue];} else {let result performExpensiveOperation(newValue); // 假设这是一个耗时的操作cache[newValue] result;return result;}};
}let memoizedCalc expensiveCalculation();
memoizedCalc(10); // 第一次计算
memoizedCalc(10); // 第二次从缓存中获取结果
二、代理
1、定义
JS中代理是ES6ECMAScript 2015中引入的一个新特性。它提供了一种机制通过该机制可以拦截和修改对目标对象的访问和操作。
2、工作原理
代理对象充当了一个“中间人”的角色它拦截了对目标对象的操作并在操作执行前后添加自定义的行为。代理对象通过构造函数Proxy来创建该构造函数接受两个参数目标对象和一个处理器对象称为handler。
3、代理对象简介
3.1、创建语法 let proxy new Proxy(target, handler); target被代理的目标对象。
handler定义代理行为的对象。处理器对象中可以定义各种陷阱函数trap用于拦截对目标对象的操作。
3.2、处理器对象handler介绍
处理器对象中可以定义多种陷阱函数每种陷阱函数对应一种基本操作。如下表所示标红是常用的
陷阱函数描述参数get(target, propKey, receiver)拦截对目标对象属性的读取操作。 - target目标对象。 - propKey要读取的属性名。 - receiver最初接收调用的对象通常是Proxy实例本身。 set(target, propKey, value, receiver)拦截对目标对象属性的设置操作。 - target目标对象。 - propKey要设置的属性名。 - value要设置的新属性值。 - receiver最初接收调用的对象通常是Proxy实例本身。 has(target, propKey)拦截对目标对象属性的存在性检查使用in操作符。 - target目标对象。 - propKey要检查的属性名。 deleteProperty(target, propKey)拦截对目标对象属性的删除操作使用delete操作符。 - target目标对象。 - propKey要删除的属性名。 apply(target, thisArg, argumentsList)拦截对目标函数的调用操作。 - target目标函数。 - thisArg函数调用时使用的this值。 - argumentsList函数调用时传递的参数列表类数组对象。 construct(target, argumentsList, newTarget)拦截使用new操作符创建目标对象的实例的操作。 - target目标构造函数。 - argumentsList调用new时传递的参数列表类数组对象。 - newTarget最初接收调用的构造函数通常是Proxy实例本身。 getOwnPropertyDescriptor(target, propKey)拦截对目标对象属性的Object.getOwnPropertyDescriptor方法的调用。 - target目标对象。 - propKey要获取描述符的属性名。 defineProperty(target, propKey, descriptor)拦截对目标对象属性的Object.defineProperty方法的调用。 - target目标对象。 - propKey要定义的属性名。 - descriptor属性描述符对象。 getPrototypeOf(target)拦截对目标对象的Object.getPrototypeOf方法的调用。- target目标对象。setPrototypeOf(target, proto)拦截对目标对象的Object.setPrototypeOf方法的调用。 - target目标对象。 - proto要设置的新原型。 isExtensible(target)拦截对目标对象的Object.isExtensible方法的调用。- target目标对象。preventExtensions(target)拦截对目标对象的Object.preventExtensions方法的调用。- target目标对象。getOwnPropertyNames(target)拦截对目标对象的Object.getOwnPropertyNames方法的调用。- target目标对象。getOwnPropertySymbols(target)拦截对目标对象的Object.getOwnPropertySymbols方法的调用。- target目标对象。
4、代理的应用场景
数据验证可以拦截赋值操作并在赋值前检查值的类型和范围等。确保数据的准确性
懒加载也就是延迟加载当需要访问某个属性时才加载该属性的值。这可以减少初始加载时间提高应用性能。
对象封装它可以提供统一的访问接口隐藏对象的内部实现细节。例如可以拦截对目标对象的所有操作并在操作执行前后添加日志记录或权限检查。
日志用于记录对对象的所有操作以便进行调试和监控。通过拦截和记录对对象的各种操作可以方便地跟踪对象的状态变化。
数据绑定代理可以用于实现数据绑定。当数据对象的属性发生变化时可以自动更新到界面上提高开发效率和代码的可维护性。
权限控制用于控制对对象的访问权限。例如可以拦截对目标对象的属性读取操作并根据用户的权限决定是否允许读取。在JS宏中没啥用因为允不允许访问数据始终在表格里面安全性不高
5、示例代码
let target {name: Alice,age: 25
};let handler {get(target, prop, receiver) {console.log(Reading ${prop});return Reflect.get(target, prop, receiver);},set(target, prop, value, receiver) {console.log(Writing ${prop});return Reflect.set(target, prop, value, receiver);}
};let proxy new Proxy(target, handler);console.log(proxy.name); // 输出: Reading name, Alice
proxy.age 30; // 输出: Writing age
console.log(proxy.age); // 输出: Reading age, 30
上述是一个简单的代理示例展示了如何拦截和修改对目标对象的属性读取和赋值操作。
三、闭包与代理的比较
闭包代理定义能够访问另一个函数作用域中的变量的函数拦截对象上的操作并在它们被执行之前或之后执行自定义代码的对象特点函数嵌套函数访问外部变量变量持久化拦截对象操作自定义行为优点封装数据避免全局变量污染实现私有方法和变量增强对象功能数据验证日志记录缺点内存消耗性能问题性能开销应用场景数据隐藏与封装工厂函数模拟私有方法和变量数据验证与格式化访问控制事件监听
四、总结
闭包和代理是JavaScript中两种强大的特性它们各自具有独特的功能和用途。闭包主要用于数据隐藏与封装、工厂函数以及模拟私有方法和变量等场景而代理则更适用于增强对象功能、数据验证、日志记录以及访问控制等场景。在实际开发中可以根据具体需求选择合适的特性来实现所需的功能。