闭包和作用域
- 闭包就是能够读取其他函数部变量的函数。由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,容因此可以把闭包简单理解成”定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
- 作用域被分为 全局作用域、函数作用域、块级作用域,全局作用域定义的变量全局可以访问,函数作用域只有在函数内部可以使用,块级作用域用大括号包起来的范围被称为块级作用域,每个作用域可以访问自己父级作用域的函数或者变量,而父级作用域不能访问自己子级作用域的变量和函数。
环境和作用域
- 环境:代码运行所依赖的东西称之为环境
- 环境存在的价值:被需要才会体现出价值
- 作用域:全局作用域只有一个,每个函数都有自己的作用域
- 1:编译器运行时会将变量定义在所在的作用域
- 2:使用变量时会从当前作用域查找变量,没有时会层层网上找
- 3:作用域就像攀亲戚,晚辈可以向长辈要东西,但是长辈不能向晚辈要东西
下面代码中 show 方法被定义在全局作用域中,因而在外部可以使用 show(),但是 show2 方法被定义在 show 方法内部,所以在 show 方法中可以使用,但是在全局环境下不能使用。
| 12
 3
 4
 5
 6
 7
 8
 
 | let name = "李四";function show() {
 function show2() {
 console.log(name);
 }
 show2();
 }
 show();
 
 | 
函数的环境和函数的作用域
函数执行多次,里面的内存地址也会被声明多个内存地址,执行完一次后,里面的内存地址被清理掉
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | let name = "李四";function show() {
 let age = "18";
 function getname() {
 let height = ",188cm";
 console.log(name + age + height);
 }
 getname();
 
 
 
 }
 
 
 show();
 show();
 show();
 
 | 
延伸函数环境生命周期
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 
 | function sum() {
 let n = 1;
 function show() {
 console.log(++n);
 }
 show();
 }
 
 sum();
 sum();
 sum();
 sum();
 
 console.warn("分割线");
 
 
 function sum2() {
 let a = 1;
 return () => {
 console.log(++a);
 };
 }
 
 
 
 const a2 = sum2();
 a2();
 a2();
 a2();
 a2();
 
 console.warn("分割线");
 
 
 function sum3() {
 let a = 1;
 return () => {
 let m = 1;
 function addm() {
 
 console.log(`a:${++a}`);
 
 console.log(`m:${++m}`);
 }
 addm();
 };
 }
 
 
 
 
 const addsum = sum3();
 addsum();
 addsum();
 addsum();
 addsum();
 
 | 
构造函数中的作用域的使用形态
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | function show() {
 this.a = 1;
 this.sum = () => {
 console.log(++this.a);
 };
 }
 
 let a = new show();
 console.log(a);
 a.sum();
 a.sum();
 a.sum();
 
 console.warn("分割线");
 
 
 function show2() {
 let a = 1;
 sum = () => {
 console.log(++a);
 };
 return {
 a,
 sum,
 };
 }
 let a1 = show2();
 console.log(a1);
 
 a1.sum();
 a1.sum();
 a1.sum();
 
 | 
块级作用域
- es6 新增 let const
- 两者都是块级作用域,可以理解为一个大括号就是一个块级作用域
- 不同点:let 定义的变量的值可以被更改,const 定义的变量不可以被更改,
 但是改变相同引用地址里免的值可以被更改
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | var a = "这是全局a";{
 let b = 1;
 }
 {
 let b = 1;
 }
 
 console.log(a);
 
 
 console.log(b);
 
 | 
let 和 var 在 for 循环中的不同反应
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | for (var i = 1; i <= 3; i++) {
 
 
 
 setTimeout(() => {
 console.log(i);
 }, 1000);
 }
 
 for (let a = 1; a <= 3; a++) {
 
 
 setTimeout(() => {
 console.log(a);
 }, 1000);
 }
 
 | 
利用自执行函数将 var 变成伪块级作用域
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | for (var a = 0; a <= 5; a++) {
 
 (function(a) {
 setTimeout(() => {
 console.log(a);
 }, 1000);
 })(a);
 }
 
 | 
闭包内存泄漏的解决办法
| 12
 
 | <div name="张三">zhangsan</div><div name="李四">lisi</div>
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | let div = document.querySelectorAll("div");div.forEach((item) => {
 
 let desc = item.getAttribute("name");
 item.addEventListener("click", () => {
 
 console.log(desc);
 console.log(item);
 });
 
 item = null;
 });
 
 | 
this 在闭包中的历史遗留问题
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | let obj = {state: "lisi",
 
 get: function() {
 
 console.log(this);
 
 return function() {
 
 console.log(this);
 return this.state;
 };
 },
 };
 
 let a = obj.get();
 
 console.log(a());
 
 console.warn("----分隔线----");
 
 let user = {
 name: "李四",
 getname: function() {
 return () => {
 return this;
 };
 },
 };
 console.log(user.getname()());
 
 | 
多级作用域嵌套原理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | let arr = [];
 
 for (let a = 1; a <= 5; a++) {
 arr.push(() => {
 return a;
 });
 }
 console.log(arr[4]());
 
 let arr2 = [];
 
 
 for (var a = 1; a <= 5; a++) {
 arr2.push(() => {
 return a;
 });
 }
 console.log(arr2[4]());
 
 |