这次把JS闭包给你讲得明明白白

  • A+
所属分类:Web前端
摘要

什么是环境与作用域: 函数的环境与作用域原理: 延伸函数环境生命周期:

什么是环境与作用域:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>     <h1>CYY</h1>      <script>         let name = 'cyy';//全局环境,不会回收     </script> </body>  </html>

 

函数的环境与作用域原理:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>     <h1>CYY</h1>      <script>         let name = 'cyy';//全局环境,不会回收          function func() {             // 函数环境             let name = 'cyy2';         }         // 只在函数被调用时创建,函数执行结束后会被销毁          // 函数外部不能使用函数内部的变量,函数内部可以使用函数外部的变量     </script> </body>  </html>

 

延伸函数环境生命周期:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // 函数执行完之后,内部变量就会被销毁,除非一直在被引用         // function func() {         //     let n = 1;         //     function show() {         //         console.log(++n);         //     }         //     show()         // }         // func(); func();           // function func() {         //     let n = 1;         //     return function show() {         //         console.log(++n);         //     }         //     show()         // }         // // 每次调用会创建新的内存地址         // let res = func(); // 等于把show函数返回给外部         // res(); res(); res(); res();         // // 每次调用会创建新的内存地址         // let res2 = func(); res2();           // 不会递增,因为render方法并没有被外部引用,所以每次调用都会创建新的内存空间,无法保存上一次的变量         // function func() {         //     let n = 1;         //     return function show() {         //         let m = 1;         //         function render() {         //             console.log(++m);         //         }         //         render()         //     }         //     show()         // }         // let res = func(); res(); res(); res(); res();          function func() {             let n = 1;             return function show() {                 let m = 1;                 return function render() {                     console.log('m:' + ++m);                     console.log('n:' + ++n);                 }                 render()             }             show()         }         let res = func()(); // 等于把render函数返回给外部         res(); res(); res(); res();     </script> </body>  </html>

 

构造函数中的作用域的使用形态:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // 构造函数中的方法一直在被引用,因此变量能够保存         // function Show() {         //     let n = 1;         //     this.render = function () {         //         console.log(++n);         //     }         // }         // let s = new Show();         // s.render();         // s.render();          // 与上述写法类似         // function Show() {         //     let n = 1;         //     function render() {         //         console.log(++n);         //     }         //     return {         //         render: render         //     }         // }         // let s = new Show();         // s.render();         // s.render();           function Show() {             let n = 1;             this.render = function () {                 console.log(++n);             }         }         // 每次new都会开辟一块新的空间,因此变量无法被继续保存         let s = new Show(); s.render(); s.render();         let s2 = new Show(); s2.render(); s2.render();     </script> </body>  </html>

 

什么是块级作用域:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // {         //     let a = 1;         //     console.log(a);         // }         // // console.log(a);          // {         //     let a = 1;         //     console.log(a);         // }  // var没有块级作用域 //         { //             var a = 1; //         } //         console.log(a);     </script> </body>  </html>

 

let-var在for循环中的执行原理:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // var没有块级作用域,因此i是挂载到全局的,每次循环都改变了全局的i         // for (var i = 0; i <= 3; i++) {         //     console.log(111);         // }         // console.log(i);         // console.log(window.i);          // let有块级作用域,因此只在for循环中,在全局无法调用         //         for (let i = 0; i <= 3; i++) {         //             console.log(111);         //         }         //         console.log(i);          for (var i = 0; i <= 3; i++) {             setTimeout(function () {                 console.log(i);//一直输出全局的i,是4             });         }          for (let i = 0; i <= 3; i++) {             setTimeout(function () {                 console.log(i);             });         }     </script> </body>  </html>

 

模拟出var的伪块作用域:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // var没有块级作用域,但是有函数作用域,因此可以把程序用函数包起来,匿名函数自执行         for (var i = 0; i <= 3; i++) {             (function (i) {                 setTimeout(function () {                     console.log(i);                 });             })(i);         }     </script> </body>  </html>

 

多级作用域嵌套详解:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // let arr = [];         // for (let i = 0; i < 4; i++) {         //     arr.push(function () {         //         return i;         //     });         // }         // console.log(arr);//在数组中压入4个函数,由于函数处于被引用状态,因此内部的变量得以保留         // console.log(arr[0]());// 执行数组中第一个元素的函数         // console.log(arr[1]());// 执行数组中第一个元素的函数         // console.log(arr[2]());// 执行数组中第一个元素的函数           // let arr = [];         // for (var i = 0; i < 4; i++) {         //     arr.push(function () {         //         return i;         //     });         // }         // console.log(arr);//在数组中压入4个函数,函数操作的i始终是全局的         // console.log(arr[0]());// 执行数组中第一个元素的函数         // console.log(arr[1]());// 执行数组中第一个元素的函数         // console.log(arr[2]());// 执行数组中第一个元素的函数           // 使用函数作用域使得i不再是全局         let arr = [];         for (var i = 0; i < 4; i++) {             (function (i) {                 arr.push(function () {                     return i;                 });             })(i);          }         console.log(arr);//在数组中压入4个函数,函数操作的i始终是全局的         console.log(arr[0]());// 执行数组中第一个元素的函数         console.log(arr[1]());// 执行数组中第一个元素的函数         console.log(arr[2]());// 执行数组中第一个元素的函数     </script> </body>  </html>

 

什么是闭包:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // 闭包         function func() {             let n = 1;             return function sum() {                 console.log(++n);             }         }         let a = func();         a(); a(); a(); a();     </script> </body>  </html>

 

使用闭包获取区间商品:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>      </style> </head>  <body>      <script>         // let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];         // let res = arr.filter(item => item <= 7 && item >= 3);         // console.log(res);          // let res2 = arr.filter(item => item <= 6 && item >= 1);         // console.log(res2);          // // 闭包可以访问到父级的变量         // function between(a, b) {         //     return function (item) {         //         return item <= a && item >= b;         //     }         // }         // let res3 = arr.filter(between(7, 3));         // console.log(res3);         // let res4 = arr.filter(between(5, 2));         // console.log(res4);          let lessons = [             { title: 'title1', price: 100, click: 1 },             { title: 'title2', price: 67, click: 2 },             { title: 'title3', price: 32, click: 5 },         ];          function between(a, b) {             return function (item) {                 return item.price <= a && item.price >= b;             }         }         let res3 = lessons.filter(between(100, 50));         console.table(res3);      </script> </body>  </html>

 

移动动画的闭包使用:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>         button {             position: absolute;         }     </style> </head>  <body>     <button message="cyy1">cyy1</button>     <button message="cyy2">cyy2</button>      <script>         let btns = document.querySelectorAll('button');         btns.forEach(function (btn) {             btn.addEventListener('click', function () {                 let left = 1;                 console.log(left);                 // 闭包:能够访问到外部的变量,一直向外去找                 setInterval(function () {                     btn.style.left = left++ + 'px';                 }, 10);             });         });      </script> </body>  </html>

这次把JS闭包给你讲得明明白白

 

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>         button {             position: absolute;         }     </style> </head>  <body>     <button message="cyy1">cyy1</button>     <!-- <button message="cyy2">cyy2</button> -->      <script>         // let btns = document.querySelectorAll('button');         // btns.forEach(function (btn) {         //     let left = 1; // 点击之后不会形成新的环境,解决抖动的问题;但是每点击一次会生成一个新的环境,会重复定义多个定时器,导致动画越来越快         //     let interval = false; // 节流阀,防抖         //     btn.addEventListener('click', function () {         //         if (interval == false) {         //             interval = true;         //             // let left = 1; // 每次点击后会形成新的环境,使得left从1开始运动,导致动画抖动         //             console.log(left);         //             // 闭包:能够访问到外部的变量,一直向外去找         //             setInterval(function () {         //                 btn.style.left = left++ + 'px';         //             }, 10);         //         }          //     });         // });          // let btns = document.querySelectorAll('button');         // btns.forEach(function (btn) {         //     let bind = false;         //     btn.addEventListener('click', function () {         //         if (bind == false) {         //             bind = true;         //             let left = 1;         //             // 闭包:能够访问到外部的变量,一直向外去找         //             setInterval(function () {         //                 btn.style.left = left++ + 'px';         //             }, 10);         //         }          //     });         // });          let btns = document.querySelectorAll('button');         btns.forEach(function (btn) {             let bind = false;             btn.addEventListener('click', function () {                 if (bind == false) {                     let left = 1;                     // 闭包:能够访问到外部的变量,一直向外去找                     // setInterval返回定时器编号                     bind = setInterval(function () {                         btn.style.left = left++ + 'px';                     }, 10);                     console.log(bind);                 }              });         });      </script> </body>  </html>

 

利用闭包根据字段排序商品:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>         button {             position: absolute;         }     </style> </head>  <body>      <script>          let goods = [             { name: 'name1', click: 99, price: 19 },             { name: 'name2', click: 122, price: 327 },             { name: 'name3', click: 44, price: 991 },         ];          function order(field, type = "asc") {              return function (a, b) {                 if (type == 'asc') {                     return a[field] > b[field] ? 1 : -1;                 } else {                     return a[field] > b[field] ? -1 : 1;                 }              }         }         let res = goods.sort(order('click', 'desc'));         console.table(res);       </script> </body>  </html>

这次把JS闭包给你讲得明明白白

 

 

闭包的内存泄露解决方法:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>         button {             position: absolute;         }     </style> </head>  <body>     <div desc="cyy">CYY</div>     <div desc="girl">GIRL</div>      <script>          // let divs = document.querySelectorAll('div');         // divs.forEach(function (div) {         //     div.addEventListener('click', function () {         //         console.log(div.getAttribute('desc'));         //         console.log(div); // 存在内存泄露问题         //     });         // });          let divs = document.querySelectorAll('div');         divs.forEach(function (div) {             let desc = div.getAttribute('desc');             div.addEventListener('click', function () {                 // 异步调用点击                 console.log(desc);                 console.log(div);             });             div = null; // 同步执行         });      </script> </body>  </html>

 

this在闭包中的历史遗留问题:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <title>Document</title>     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">     <style>         button {             position: absolute;         }     </style> </head>  <body>     <script>          // let obj = {         //     name: 'cyy',         //     show() {         //         return function () {         //             return this.name;         //         }         //     }         // }         // console.log(obj.show()()); // this指向window         // let func = obj.show(); func(); // 与上述写法相同          //使用箭头函数解决问题         // let obj = {         //     name: 'cyy',         //     show() {         //         return () => {         //             return this.name;         //         }         //     }         // }         // console.log(obj.show()());   // 使用bind改变this的指向         let obj = {             name: 'cyy',             show() {                 return function () {                     return this.name;                 }             }         }         let func = obj.show(); console.log(func.bind(obj)());         </script> </body>  </html>