闭包和作用域
- 闭包就是能够读取其他函数部变量的函数。由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,容因此可以把闭包简单理解成”定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
- 作用域被分为 全局作用域、函数作用域、块级作用域,全局作用域定义的变量全局可以访问,函数作用域只有在函数内部可以使用,块级作用域用大括号包起来的范围被称为块级作用域,每个作用域可以访问自己父级作用域的函数或者变量,而父级作用域不能访问自己子级作用域的变量和函数。
环境和作用域
- 环境:代码运行所依赖的东西称之为环境
- 环境存在的价值:被需要才会体现出价值
- 作用域:全局作用域只有一个,每个函数都有自己的作用域
- 1:编译器运行时会将变量定义在所在的作用域
- 2:使用变量时会从当前作用域查找变量,没有时会层层网上找
- 3:作用域就像攀亲戚,晚辈可以向长辈要东西,但是长辈不能向晚辈要东西
下面代码中 show 方法被定义在全局作用域中,因而在外部可以使用 show(),但是 show2 方法被定义在 show 方法内部,所以在 show 方法中可以使用,但是在全局环境下不能使用。
1 2 3 4 5 6 7 8
| let name = "李四"; function show() { function show2() { console.log(name); } show2(); } show();
|
函数的环境和函数的作用域
函数执行多次,里面的内存地址也会被声明多个内存地址,执行完一次后,里面的内存地址被清理掉
1 2 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();
|
延伸函数环境生命周期
1 2 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();
|
构造函数中的作用域的使用形态
1 2 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 定义的变量不可以被更改,
但是改变相同引用地址里免的值可以被更改
1 2 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 循环中的不同反应
1 2 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 变成伪块级作用域
1 2 3 4 5 6 7 8 9
| for (var a = 0; a <= 5; a++) { (function(a) { setTimeout(() => { console.log(a); }, 1000); })(a); }
|
闭包内存泄漏的解决办法
1 2
| <div name="张三">zhangsan</div> <div name="李四">lisi</div>
|
1 2 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 在闭包中的历史遗留问题
1 2 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()());
|
多级作用域嵌套原理
1 2 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]());
|