js高级-闭包 小鱼儿 2022-12-04 07:37 85阅读 0赞 # 第六章:闭包 # ## 什么是闭包 ## > 闭包就是能够读取其他函数内部变量的函数,由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 `定义在一个函数内部的函数`。 > 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 ## 闭包的好处:延展了函数的作用域 ## ## 闭包的弊端: ## > 一般情况下:函数执行完毕,立即释放函数开辟的作用域(用完就释放,节省内存) > > 但是如果使用闭包:因为内部函数还要访问外部函数作用域中的变量,外部函数执行完毕,不会立即释放开辟的作用域,造成程序性能降低,消耗内存。 闭包的用途: * 可以在函数外部读取函数内部成员 * 让函数内成员始终存活在内存中 > 闭包:在一个作用域中可以访问另一个作用域的变量 // 未发生闭包 function fn() { // 当fn函数执行时,会开启一个作用域。执行完毕后,作用域和作用域中的变量都会被销毁 var n = 10; return n; } fn(); > 发生闭包,闭包特点:延展了函数的作用域范围 function fn() { var n = 10; return function () { return n; } } var f = fn(); // fn函数执行完毕后,因为f函数还要访问fn作用域中的变量n console.log(f()); ## 闭包演示 ## ### 闭包演示:案例 1 ### > 闭包的概念:在一个作用域中可以访问另一个作用域的变量或者函数 function getRandom() { var random = parseInt(Math.random() * 10) + 1; return function () { return random; } } var fn = getRandom(); console.log(fn()); console.log(fn()); ### 闭包演示:案例 2 ### > 注意:形参n也是局部变量 function getFun(n) { return function (m) { // 一个函数只有一个返回值 且 就近返回数值 return m; } } // 求 100 + m var fn100 = getFun(100); // 求 1000 + m var fn1000 = getFun(1000); console.log(fn100(1)); console.log(fn1000(1)); ### 闭包演示:经典面试题 ### #### 面试题一:使用闭包改造代码 #### console.log('start'); for (var i = 0; i < 3; i++) { setTimeout(function () { console.log(i); }, 0); } console.log('end'); // 代码执行顺序:start -> end -> 结果为3(出现3次) // ************** 需要:使用闭包改造上面代码,要求依次输出:0 1 2 ************ console.log('start'); for (var i = 0; i < 3; i++) { (function (i) { setTimeout(function () { console.log(i); }, 0); })(i); } console.log('end'); // 代码执行顺序:start -> end -> 依次:0 1 2 #### 面试题二:点击不同的按钮,改变页面字体大小 #### <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> 我是字体的演示 <button id="btn1">按钮1</button> <button id="btn2">按钮2</button> <button id="btn3">按钮3</button> <script> // ..........传统方法1:................ var btn1 = document.getElementById('btn1'); var btn2 = document.getElementById('btn2'); var btn3 = document.getElementById('btn3'); btn1.onclick = function() { document.body.style.fontSize = '12px'; } btn2.onclick = function() { document.body.style.fontSize = '14px'; } btn3.onclick = function() { document.body.style.fontSize = '16px'; }; // ..........方法2:闭包的应用............. // 创建一个函数,设置body的字体大小 var btn1 = document.getElementById('btn1'); var btn2 = document.getElementById('btn2'); var btn3 = document.getElementById('btn3'); // 创建一个函数makeFun,设置body的字体大小 function makeFun(size) { return function () { // 函数不需要传参数? 如果本身没有传入参数可以去上级去找 document.body.style.fontSize = size + 'px'; } } // 真正执行点击事件的函数是:函数makeFun返回的函数 btn1.onclick = makeFun(12); btn2.onclick = makeFun(14); btn3.onclick = makeFun(16); </script> </body> </html> #### 面试题二:点击不同的按钮,改变页面字体大小(闭包写法) #### <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> 我是字体的演示 <div id="box"> <button size="12">按钮1</button> <button size="14">按钮2</button> <button size="16">按钮3</button> </div> <script> // ...................闭包的应用................ var box = document.getElementById('box'); var buttons = box.children; // 创建一个函数makeFun,设置body的字体大小 function makeFun(size) { return function () { // 不需要传参数? 如果本身没有传入参数可以去上级去找(js基础作用域链) document.body.style.fontSize = size + 'px'; console.log(size); } } for (var i = 0; i < buttons.length; i++) { var btn = buttons[i]; // 获取标签的自定义属性getAttribute var size = btn.getAttribute('size'); btn.onclick = makeFun(size); //点击的时候才会调用return返回的匿名函数 } </script> </body> </html> > 需求:点击li的时候输出当前li对应的索引(闭包实现) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <ul id="heroes"> <li>安琪拉</li> <li>李白</li> <li>诸葛亮</li> <li>狄仁杰</li> </ul> <script> // .......推荐方式1:给li注册点击事件............... // 需求:点击li的时候输出当前li对应的索引 var heroes = document.getElementById('heroes'); var list = heroes.children; for (var i = 0; i < list.length; i++) { var li = list[i]; li.index = i; // 让对象的自定义属性:每一次循环,记录每次的i li.onclick = function () { // 2 点击li的时候输出当前li对应的索引 console.log(this.index); } } // .......方式2:给li注册点击事件.................... // 需求:点击li的时候输出当前li对应的索引 var heroes = document.getElementById('heroes'); var list = heroes.children; for (var i = 0; i < list.length; i++) { var li = list[i]; // 每一个i,对应一个作用域 // 内部的function还要访问外部的变量i;延展了作用域 (function (i) { li.onclick = function () { // 2 点击li的时候输出当前li对应的索引 console.log(i); } })(i); } </script> </body> </html> ## 闭包的思考题 ## 思考题 1:`没有发生闭包` // 思考1:返回值是函数,不一定会存在闭包 // 闭包:一个作用域可以访问另一个作用域的变量(不能是全局作用域)或者函数 var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; // 函数中的this指向调用者fn(),所以this--->window }; } }; var fn = object.getNameFunc() fn(); console.log(object.getNameFunc()()); // 结果是:The Window 思考题 2:`发生了闭包`,闭包发生在`getNameFunc`函数 var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { var that = this; // this指向object,将this赋值给that,此时that也指向object return function () { return that.name; // that也指向object }; } }; console.log(object.getNameFunc()()); // My Object
还没有评论,来说两句吧...