天津网站建设交易,怎么做购物优惠券网站,中小型企业网络设计,最好的编程培训机构给大家推荐一个实用面试题库
1、前端面试题库 #xff08;面试必备#xff09; 推荐#xff1a;★★★★★
地址#xff1a;web前端面试题库
闭包是什么#xff1f;
闭包是指一个函数可以访问并操作其词法作用域外的变量的能力。闭包就是能够读取其他函数内…给大家推荐一个实用面试题库
1、前端面试题库 面试必备 推荐★★★★★
地址web前端面试题库
闭包是什么
闭包是指一个函数可以访问并操作其词法作用域外的变量的能力。闭包就是能够读取其他函数内部变量的函数。例如在javascript中只有函数内部的子函数才能读取局部变量所以闭包可以理解成“定义在一个函数内部的函数“。在本质上闭包是将函数内部和函数外部连接起来的桥梁。
特点函数嵌套并返回子函数子函数访问了外变量。
//外部函数
function outerFunction() {//内部函数外的变量var outerVariable I am from outer function; //返回内部函数function innerFunction() { console.log(outerVariable); } return innerFunction;
} // outerFunction执行完之后被销毁但是outerVariable被引用着所以仍然活着
var closure outerFunction(); closure(); // 输出I am from outer function闭包的作用
封装私有变量闭包可以用于创建私有变量和方法。通过在函数内部定义变量并返回一个内部函数外部无法直接访问这些变量从而实现了封装的效果。这样可以避免全局变量的污染提高代码的可维护性和安全性。延迟执行闭包可以用于实现延迟执行的效果。通过在函数内部定义一个定时器或事件监听器并返回一个内部函数可以在需要的时候触发执行。记忆化/保持状态闭包可以用于实现记忆化的效果即将函数的计算结果缓存起来以便在后续调用时直接返回缓存的结果提高函数的执行效率。回调函数闭包可以用于实现回调函数。通过将一个函数作为参数传递给另一个函数并在内部函数中调用该函数可以实现异步操作的回调机制。模块化开发闭包可以用于实现模块化开发。通过将一组相关的变量和方法封装在一个闭包中可以避免全局命名空间的污染实现模块的独立性和复用性。
闭包的缺陷
内存占用闭包会导致外部函数的变量无法被垃圾回收从而增加内存占用。如果闭包会长时间存在那么外部变量将无法被释放可能导致内存泄漏。性能损耗闭包涉及到作用域链的查找过程会带来一定的性能损耗。在性能要求高的场景下需要注意闭包的使用。
闭包的应用场景
参考链接 js闭包的6种应用场景这下会用了
1. 自执行函数可以实现单例模式
let say (function(){let val hello world;function say(){console.log(val);}return say;
})()
var Singleton (function () {var instance;function createInstance() {var object new Object(I am the instance);return object;}return {getInstance: function () {if (!instance) {instance createInstance();}return instance;},};})();2. 防抖节流
// 节流函数封装
function throttle(func, delay) {let timer null;return function () {if (!timer) {timer setTimeout(() {func.apply(this, arguments);timer null;}, delay);}};
}// 防抖函数封装
function debounce(func, delay) {let timer null;return function () {clearTimeout(timer);timer setTimeout(() {func.apply(this, arguments);}, delay);};
}
3. 函数柯里化
//柯里化前
function add(a, b, c) {return a b c;
}
console.log(add(1, 2, 3)); //6//柯里化后
function addCurried1(a) {return function (b) {return function (c) {return a b c;};};
}//箭头函数简写
const addCurried2 (a) (b) (c) a b c;
console.log(addCurried1(1)(2)(3)); //6
console.log(addCurried2(1)(2)(3)); //6
4. 发布订阅
function createPubSub() {// 存储事件及其对应的订阅者const subscribers {};// 订阅事件function subscribe(event, callback) {// 如果事件不存在则创建一个新的空数组if (!subscribers[event]) {subscribers[event] [];}// 将回调函数添加到订阅者数组中subscribers[event].push(callback);}// 发布事件function publish(event, data) {// 如果事件不存在则直接返回if (!subscribers[event]) {return;}// 遍历订阅者数组调用每个订阅者的回调函数subscribers[event].forEach((callback) {callback(data);});}// 返回订阅和发布函数return {subscribe,publish,};
}// 使用示例
const pubSub createPubSub();// 订阅事件
pubSub.subscribe(event1, (data) {console.log(订阅者1收到事件1的数据:, data);
});pubSub.subscribe(event2, (data) {console.log(订阅者2收到事件2的数据:, data);
});// 发布事件
pubSub.publish(event1, Hello);
// 输出: 订阅者1收到事件1的数据: HellopubSub.publish(event2, World);
// 输出: 订阅者2收到事件2的数据: World
5. 迭代器
function createIterator(arr) {let index 0;return {next: function() {if (index arr.length) {return {value: arr[index],done: false};} else {return {done: true};}}};
}const myIterator createIterator([1, 2, 3]);console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { done: true }
如何释放闭包
释放闭包通常是通过解除对闭包的引用来实现的。当不再需要使用闭包时可以将闭包所在的变量设置为 null 或者将闭包所在的变量赋予其他值从而断开对闭包的引用。这样JavaScript 引擎在下一次垃圾回收时会判断闭包不再被引用从而释放闭包占用的内存空间。
相关概念
函数的词法作用域也叫静态作用域/闭包作用域
是指在函数定义时确定的作用域而不是在函数调用时确定的作用域。它是由函数在定义时所处的上下文环境决定的与函数的调用位置无关。
规则
函数内部可以访问函数外部的变量函数内部的变量在函数外部不可访问函数内部可以访问函数外部的函数函数内部的函数可以访问外部函数的变量
词法作用域的优势在于它提供了更可靠和可预测的变量访问方式。在函数定义时就确定了函数内部可以访问的变量不会受到函数调用位置的影响。这种静态作用域的特性使得代码更易于理解和维护并且可以实现一些高级的编程技巧如闭包和模块化开发。
执行上下文
每当 JavaScript 代码执行时都会创建一个执行上下文是 JavaScript 引擎内部的一种数据结构用于管理代码的执行环境、变量的作用域并按照特定的规则进行管理和销毁。
执行上下文可以分为三种类型
全局执行上下文Global Execution Context全局执行上下文是在整个脚本文件执行时创建的它是最外层的执行上下文。在全局执行上下文中变量和函数声明会被提升并且会创建全局对象如浏览器环境中的 window 对象和全局变量。函数执行上下文Function Execution Context每当函数被调用时都会创建一个函数执行上下文。函数执行上下文中包含了函数的参数、局部变量、函数内部的变量和函数声明。每个函数执行上下文都有自己的作用域链和 this 值。Eval 执行上下文Eval Execution Context在使用 eval() 函数执行代码时会创建一个 eval 执行上下文。它与全局执行上下文类似但是它有自己的词法作用域。
执行上下文的生命周期包括以下阶段
创建阶段Creation Phase在这个阶段JavaScript 引擎会创建执行上下文并进行变量和函数的声明。变量会被初始化为 undefined函数会被存储在内存中。执行阶段Execution Phase在这个阶段JavaScript 引擎会按照代码的顺序执行语句给变量赋值执行函数调用等操作。
观察和理解执行上下文
执行上下文的管理和切换由 JavaScript 引擎自动完成开发者可以通过了解执行上下文的概念和规则更好地理解代码的执行过程以及变量和函数的作用范围。
尽管无法直接访问或输出执行上下文但我们可以通过一些间接的方式来观察和理解执行上下文的行为和特性
使用 console.log()可以在代码中使用 console.log() 方法输出变量的值、函数的执行结果等信息从而间接观察到执行上下文的影响。使用调试工具现代的浏览器和开发工具提供了强大的调试功能可以在代码执行过程中查看执行上下文的变化、变量的值以及调用栈等信息。使用闭包通过创建闭包我们可以间接地观察到执行上下文的作用。闭包可以让内部函数访问外部函数的变量从而形成一个包含外部执行上下文的闭包执行上下文。
给大家推荐一个实用面试题库
1、前端面试题库 面试必备 推荐★★★★★
地址web前端面试题库