Skip to content

Commit b965f83

Browse files
this-closure
1 parent d5c5778 commit b965f83

File tree

8 files changed

+609
-3
lines changed

8 files changed

+609
-3
lines changed

Note/closure/closure.js

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
(function() {
2+
3+
function fn() {
4+
// a 为函数作用域, 在 a 声明后, 函数体内均可访问该变量
5+
const a = 'a';
6+
console.log(a);
7+
}
8+
fn();
9+
10+
// 变量 b 挂载在全局对象上, 其作用域是全局的, 无论嵌套多少层均可访问
11+
global.b = 'b';
12+
console.log(b);
13+
14+
}); //();
15+
16+
(function(){
17+
18+
// 块级作用域, 作用范围为代码块内{},
19+
// ES6 之前, js 只有函数作用域, 部分使用场景必须使用闭包来进行变量保持
20+
// ES6 引入 let/const 关键字, 这两个关键字声明的变量具备块级作用域
21+
22+
function f1() {
23+
// var 存在变量提升, 函数作用域内可在声明代码之前访问该变量
24+
console.log(a); // undefined
25+
var a = 1;
26+
console.log(a); // 1
27+
28+
console.log(b); // undefied
29+
// var 没有块级作用域, 此代码块无效
30+
{
31+
var b = 2;
32+
}
33+
console.log(b); // 2
34+
}
35+
f1();
36+
37+
function f2() {
38+
// 使用 let/const 存在暂时性死区, 在声明代码之前访问会报引用错误
39+
// console.log(a); // 暂时性死区
40+
// ReferenceError: a is not defined
41+
const a = 1;
42+
console.log(a);
43+
44+
{
45+
const b = 2;
46+
console.log(b); // 块级作用域内有效
47+
}
48+
// console.log(b); // b 在代码块中声明, 使用 const 具备块级作用域, 故超出代码块该变量已失效
49+
// ReferenceError: b is not defined
50+
}
51+
f2();
52+
}); // ();
53+
54+
55+
(function(){
56+
'use strict'
57+
58+
function f1() {
59+
// 变量 a 由 var 声明, 存在变量提升, 声明提前, 函数体内均可访问
60+
console.log(a); // undefind
61+
62+
// 变量 b 由 const 声明, 具备块级作用域, 不存在变量提升. 在函数开始, 至变量 b 声明之前, b 变量不可访问, 这块区域称为暂时性死区
63+
//console.log(b); // ReferenceError: b is not defined
64+
65+
var a = 1;
66+
const b = 2;
67+
68+
console.log(`${a}, ${b}`);
69+
}
70+
f1();
71+
72+
// 函数 f2 这也是一种暂时性死区, arg2 尚未声明, 故不能使用
73+
function f2(arg1 = arg2, arg2) {
74+
console.log(`${arg1} ${arg2}`);
75+
}
76+
f2(undefined, 1); // ReferenceError: arg2 is not defined
77+
78+
79+
80+
})();
81+
82+
83+
84+
85+
86+
87+
88+
89+
90+
91+
92+

Note/closure/closure.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
## 闭包
2+
3+
### 前置概念
4+
#### 作用域
5+
**作用域实际上是一套规则,用于确定在特定场景下如何查找变量**
6+
7+
* 函数作用域与全局作用域
8+
* 函数作用域则表示在函数体内有效
9+
* 全局作用域,则在全局生效
10+
11+
示例代码
12+
13+
```js
14+
function fn() {
15+
// a 为函数作用域, 在 a 声明后, 函数体内均可访问该变量
16+
const a = 'a';
17+
console.log(a);
18+
}
19+
fn();
20+
21+
// 变量 b 挂载在全局对象上, 其作用域是全局的, 无论嵌套多少层均可访问
22+
global.b = 'b';
23+
console.log(b);
24+
```
25+
26+
* 块级作用域
27+
* 作用于代码块内({})
28+
* 示例代码
29+
30+
```js
31+
// 块级作用域, 作用范围为代码块内{},
32+
// ES6 之前, js 只有函数作用域, 部分使用场景必须使用闭包来进行变量保持
33+
// ES6 引入 let/const 关键字, 这两个关键字声明的变量具备块级作用域
34+
35+
function f1() {
36+
// var 存在变量提升, 函数作用域内可在声明代码之前访问该变量
37+
console.log(a); // undefined
38+
var a = 1;
39+
console.log(a); // 1
40+
41+
console.log(b); // undefied
42+
// var 没有块级作用域, 此代码块无效
43+
{
44+
var b = 2;
45+
}
46+
console.log(b); // 2
47+
}
48+
f1();
49+
50+
function f2() {
51+
// 使用 let/const 存在暂时性死区, 在声明代码之前访问会报引用错误
52+
// console.log(a); // 暂时性死区
53+
// ReferenceError: a is not defined
54+
const a = 1;
55+
console.log(a);
56+
57+
{
58+
const b = 2;
59+
console.log(b); // 块级作用域内有效
60+
}
61+
// console.log(b); // b 在代码块中声明, 使用 const 具备块级作用域, 故超出代码块该变量已失效
62+
// ReferenceError: b is not defined
63+
}
64+
f2();
65+
```
66+
* 暂时性死区
67+
* 作用域内,变量声明前,不可访问该变量,这块区域称为暂时性死区
68+
* 示例代码
69+
70+
```js
71+
function f1() {
72+
// 变量 a 由 var 声明, 存在变量提升, 声明提前, 函数体内均可访问
73+
console.log(a); // undefind
74+
75+
// 变量 b 由 const 声明, 具备块级作用域, 不存在变量提升. 在函数开始, 至变量 b 声明之前, b 变量不可访问, 这块区域称为暂时性死区
76+
//console.log(b); // ReferenceError: b is not defined
77+
78+
var a = 1;
79+
const b = 2;
80+
81+
console.log(`${a}, ${b}`);
82+
}
83+
f1();
84+
85+
// 函数 f2 这也是一种暂时性死区, arg2 尚未声明, 故不能使用
86+
function f2(arg1 = arg2, arg2) {
87+
console.log(`${arg1} ${arg2}`);
88+
}
89+
f2(undefined, 1); // ReferenceError: arg2 is not defined
90+
```
91+
92+
#### js 执行
93+
* 执行步骤
94+
* 预编译阶段
95+
* 执行阶段
96+
* 执行上下文与调用栈
97+
98+
### 闭包
99+
100+
#### 闭包定义与示例
101+
102+
#### 内存管理
103+
104+
#### 内存泄露
105+
106+
#### 垃圾回收
107+
108+
### 应用场景
109+
110+
### 参考资料
111+
* [老司机也会在闭包相关知识点翻车](https://gitbook.cn/gitchat/column/5c91c813968b1d64b1e08fde/topic/5c99a9a3ccb24267c1d01960)
112+

Note/this/apply.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// 完整实现 https://github.com/wang-jun-coder/node-every-day/blob/d5c57789152db6be0a80ac7a5cb4e66ec5790580/js-%E5%9F%BA%E7%A1%80/simulate/apply.js#L25
2+
function apply(obj, args) {
3+
if ('function' !== typeof this) throw new TypeError('apply must be called by function');
4+
5+
const target = new Object(obj);
6+
const key = Symbol('target-key');
7+
8+
target[key] = this;
9+
10+
const ret = target[key](...args);
11+
delete target[key];
12+
return ret;
13+
}
14+
15+
Function.prototype.c_apply = Function.prototype.c_apply || apply;
16+
17+
18+
function fn(a, b) {
19+
this.c = a+b;
20+
}
21+
22+
23+
const a = {};
24+
fn.c_apply(a, [1, 2]);
25+
console.log(a);
26+
27+

Note/this/bind.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function bind(obj, ...args) {
2+
if ('function' !== typeof this) throw new TypeError('bind must be called by function');
3+
4+
const self = this;
5+
// 构造原型
6+
function F(){};
7+
F.prototype = this;
8+
9+
function bound(...innerArgs) {
10+
return self.apply(this.prototype && this.instanceof(F) ? this : obj || this, [...args, ...innerArgs]);
11+
}
12+
// new 一个实例, 避免原型污染
13+
bound.prototype = new F();
14+
return bound;
15+
}
16+
17+
Function.prototype.c_bind = Function.prototype.c_bind || bind;
18+
19+
20+
function fn(a, b) {
21+
this.c = a +b;
22+
}
23+
24+
const a = {};
25+
26+
fn.c_bind(a)(1,2);
27+
console.log(a);

Note/this/call.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// https://github.com/wang-jun-coder/node-every-day/blob/d5c57789152db6be0a80ac7a5cb4e66ec5790580/js-%E5%9F%BA%E7%A1%80/simulate/call.js#L1
2+
function call(obj, ...args) {
3+
4+
if ('function' !== typeof this) throw new TypeError('call must be called by function');
5+
6+
const target = new Object(obj);
7+
const key = Symbol('target-key');
8+
target[key] = this;
9+
10+
const ret = target[key](...args);
11+
delete target[key];
12+
return ret;
13+
}
14+
15+
Function.prototype.c_call = Function.prototype.c_call || call;
16+
17+
function fn(a, b) {
18+
this.c = a+b;
19+
}
20+
21+
const a = {};
22+
fn.call(a, 1, 2);
23+
console.log(a);

Note/this/hook.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const express = require('express');
2+
const app = express();
3+
app.use(function(req, res, next) {
4+
// 利用 apply/call 可以 hook 函数的对应事件, 比如此处用于统计请求时间
5+
console.time(`${req.method} ${req.url}`);
6+
const end = res.end;
7+
res.end = function(...args) {
8+
end.apply(res, args);
9+
console.timeEnd(`${req.method} ${req.url}`);
10+
}
11+
next();
12+
});
13+
app.get('/', function(req, res, next) {
14+
res.end('hello world');
15+
});
16+
app.listen(3000);

0 commit comments

Comments
 (0)