函數(shù)執(zhí)行-作用域鏈-面試題-內(nèi)存管理
全局代碼執(zhí)行過程的回顧
var name = "why";
console.log(name);
console.log(num1);
var num1 = 20;
var num2 = 30;
var result = num1 + num2;
console.log(result);
/**
* 1.代碼被解析,V8引擎內(nèi)部會幫助我們創(chuàng)建一個對象(globalObject -> go)
* var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: undefined,
* num1: undefined,
* num2: undefined,
* result: undefined
* };
* 2.運行代碼
* >2.1.v8為了執(zhí)行代碼,v8引擎內(nèi)部會有一個執(zhí)行上下文棧(Execution Context ECStack)(函數(shù)調(diào)用棧)
* 2.2.因為我們執(zhí)行的是全局代碼,為了全局代碼能夠正常的執(zhí)行,需要創(chuàng)建全局執(zhí)行上下文(Global Context ECStack)(全局代碼需要被執(zhí)行才會創(chuàng)建)
* >2.2.1.生成一個VO(Variable Object)
* VO = var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: undefined,
* num1: undefined,
* num2: undefined,
* result: undefined
* };
* >2.2.1.開始執(zhí)行代碼
* var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: "why",
* num1: 20,
* num2: 30,
* result: 50
* };
* 如果console.log(result) => 打印50
* 如果在var name = "why"之后console.log(name) 打印why
* 如果在var name1 = "20;之前console.log(name1) 打印undefined
*/
// why
// undefined
// 50
函數(shù)執(zhí)行
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script src="./全局代碼執(zhí)行過程(函數(shù)).js"></script>
</body>
</html>
全局代碼執(zhí)行過程(函數(shù)).js
var name = "why";
console.log(num1);
var num1 = 20;
var num2 = 30;
var result = num1 + num2;
console.log(result);
console.log(window);
/**
* 1.代碼被解析,V8引擎內(nèi)部會幫助我們創(chuàng)建一個對象(globalObject -> go)
* var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: undefined,
* num1: undefined,
* num2: undefined,
* result: undefined
* };
* 2.運行代碼
* >2.1.v8為了執(zhí)行代碼,v8引擎內(nèi)部會有一個執(zhí)行上下文棧(Execution Context ECStack)(函數(shù)調(diào)用棧)
* 2.2.因為我們執(zhí)行的是全局代碼,為了全局代碼能夠正常的執(zhí)行,需要創(chuàng)建全局執(zhí)行上下文(Global Context ECStack)(全局代碼需要被執(zhí)行才會創(chuàng)建)
* >2.2.1.生成一個VO(Variable Object)
* VO = var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: undefined,
* num1: undefined,
* num2: undefined,
* result: undefined
* };
* >2.2.1.開始執(zhí)行代碼
* var globalObject = {
* String: "類",
* Date: "類",
* setTimeout: "函數(shù)",
* window: globalObject,
* name: "why",
* num1: 20,
* num2: 30,
* result: 50
* };
* 如果console.log(result) => 打印50
* 如果在var name1 = "20;之前console.log(name1) 打印undefined
* window.num1 = 20
* window.num2 = 30
* window.result = 50
*/
// undefined
// 50
// window
var name = "why";
function foo() {
console.log("foo");
}
foo();
var num1 = 20;
var num2 = 30;
var result = num1 + num2;
// foo
var name = "why";
foo();
function foo() {
console.log("foo");
}
var num1 = 20;
var num2 = 30;
var result = num1 + num2;
// foo
var name = "why";
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
console.log("foo");
}
/**
* 編譯
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: undefined,
* foo: foo的地址值 => { 地址值 內(nèi)存中開辟一塊空間,保存foo函數(shù)(父級作用域,執(zhí)行的代碼塊)
* num: undefined
* m: undefined
* n: undefined
* }
* }
* 執(zhí)行
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: "why",
* foo: foo的地址值 => {
* num: 123
* m: 10
* n: 20
* }
* }
*/
// undefined
// foo
var name = "why";
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
var name = "foo";
console.log(name);
}
/**
* 編譯
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: undefined,
* foo: foo的地址值 => { 地址值 內(nèi)存中開辟一塊空間,保存foo函數(shù)(父級作用域,執(zhí)行的代碼塊)
* num: undefined
* m: undefined
* n: undefined,
* name: undefined
* }
* }
* 執(zhí)行
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: "why",
* foo: foo的地址值 => {
* num: 123
* m: 10
* n: 20,
* name: "foo"
* }
* }
*/
// undefined
// foo
作用域鏈
- 當(dāng)我們查找一個變量時,真實的查找路徑是沿著作用域鏈來查找的
var name = "why";
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
console.log(name);
}
/**
* 編譯
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: undefined,
* foo: foo的地址值 => { 地址值 內(nèi)存中開辟一塊空間,保存foo函數(shù)(父級作用域,執(zhí)行的代碼塊)
* num: undefined
* m: undefined
* n: undefined
* }
* }
* 執(zhí)行
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: "why",
* foo: foo的地址值 => {
* num: 123
* m: 10
* n: 20
* }
* }
*/
// undefined
// why
var name = "why";
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
function bar() {
console.log(name);
}
bar();
}
/**
* 編譯
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: undefined,
* foo: foo的地址值 => { 地址值 內(nèi)存中開辟一塊空間,保存foo函數(shù)(父級作用域,執(zhí)行的代碼塊)
* num: undefined
* m: undefined
* n: undefined,
* bar: bar的地址 => { 地址值 內(nèi)存中開辟一塊空間,保存bar函數(shù)(父級作用域,執(zhí)行的代碼塊}
* }
* }
* 執(zhí)行
* var GlobalObject = {
* String: "類",
* window: GlobalObject,
* name: "why",
* foo: foo的地址值 => {
* num: 123
* m: 10
* n: 20
* bar: bar的地址值 => {}
* }
* }
*/
// undefined
// why
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
function bar() {
console.log(name);
}
bar();
}
// name is not defined
var age = 100;
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
function bar() {
console.log(age);
}
bar();
}
// undefined
// 100
foo(123);
function foo(num) {
console.log(m);
var m = 10;
var n = 20;
function bar() {
console.log(age);
}
bar();
}
// age is not defined
var message = "Hello Global";
function foo() {
console.log(message);
}
function bar() {
var message = "Hello Bar";
foo();
}
bar();
// Hello Global
var message = "Hello Global";
function foo() {
console.log(message);
}
function bar() {
var message = "Hello Bar";
foo();
}
bar();
/**
* 編譯
* var GlobalObject = {
* window: GlobalObject,
* message: undefined,
* foo: foo的地址值,
* bar: bar的地址值 => {
* message: undefined,
* foo: 和之前foo的地址值相同,為同一個
* }
* }
* 執(zhí)行
* var GlobalObject = {
* window: GlobalObject,
* mesage: "Hello Global"
* foo: foo的地址值,
* bar: bar的地址值 => {
* message: "Hello Bar",
* foo: 和之前foo的地址值相同,為同一個
* }
* }
*/
// Hello Global

變量環(huán)境和記錄.png
作用域面試題
var n = 100;
function foo() {
n = 200;
}
foo();
console.log(n);
/**
* 編譯
* var GlobalObject = {
* window: GlobalObject,
* n: undefined,
* foo: foo的地址值
* }
* 執(zhí)行
* var GlobalObject = {
* window: GlobalObject,
* n: 100,
* foo: foo的地址值 => {
* n: 200
* }
* }
*/
// 200
function foo() {
console.log(n);
var n = 200;
console.log(n);
}
var n = 100;
foo();
/**
* 編譯
* var GlobalObject = {
* window: GlobalObject,
* n: undefined,
* foo: foo的地址值 => {
* n: undefined
* }
* }
* 執(zhí)行
* var GlobalObject = {
* window: GlobalObject,
* n: 100,
* foo: foo的地址值 => {
* n: 200
* }
* }
*/
// undefined
// 200
var n = 100;
function foo1() {
console.log(n);
}
function foo2() {
var n = 200;
console.log(n);
foo1();
}
foo2();
console.log(n);
/**
* 編譯
* var GlobalObject = {
* window: GlobalObject,
* n: undefined,
* foo1: foo1的地址值 => {}
* foo2: foo2的地址值 => {
* n: undefined,
* foo1: foo1的地址值 => {}
* }
* }
* 執(zhí)行
* var GlobalObject = {
* window: GlobalObject,
* n: 100,
* foo1: foo1的地址值 => {
* n: 100,
* foo1: foo1的地址值 => {}
* }
* foo2: foo2的地址值 => {
* n: 200
* }
* }
*/
// 200
// 100
// 100
var a = 100;
function foo() {
console.log(a);
return;
var a = 100;
}
foo();
/**
* 編譯
* var GlobalObject = {
* window: GlobalObject,
* n: undefined,
* foo: foo的地址值 => {
* n: undefined
* }
* }
* 執(zhí)行
* var GlobalObject = {
* window: GlobalObject,
* n: 100,
* foo: foo的地址值 => {
* n: undefined
* }
* }
*/
// undefined
function foo() {
var m = 100;
}
foo();
console.log(m);
// m is not defined
function foo() {
m = 100;
}
foo();
console.log(m);
// 100
function foo() {
var a = (b = 100);
}
foo();
console.log(a);
console.log(b);
// a is not defined
function foo() {
var a = (b = 100);
}
foo();
console.log(b);
console.log(a);
// 100
// a is not defined
function foo() {
var a = (b = 10);
/**
* var a = (b = 10); 轉(zhuǎn)化為
* var a = 10
* b = 10
*/
}
foo();
console.log(b);
console.log(a);
// 19
// a is not defined
內(nèi)存管理
內(nèi)存管理
- 不管什么樣的編程語言,在代碼的執(zhí)行過程中都是需要給它分配內(nèi)存的
- 某些編程語言需要我們自己手動的管理內(nèi)存
- 某些編程語言會可以自動幫助我們管理內(nèi)存
JavaScript的內(nèi)存結(jié)構(gòu)
- 棧結(jié)構(gòu)
- 堆結(jié)構(gòu)
var name = "why";
var age = 18;
var obj = { name: "kobe", age: 40 };
var names = ["abc", "cba"];
/**
* 棧
* var name = "why"
* var age = 18;
* var obj => 堆 返回一個地址值 0xobj
* var names => 堆 返回一個地址值 0xnames
* 堆
* 0xobj { name: "kobe", age: 40 }
* 0xnanes ["abc", "cba"]
*/
const obj = { name: "why" };
var info = { name: "kobe", friend: obj };
var p = { name: "james", friend: obj };
/**
* 棧
* var obj => 堆 0xobj
* var info => 堆 0xinfo
* var p => 0x - p
* 堆
* 0xobj { name: "why" }
* 0xinfo { name: "kobe", friend: 0xobj }
* 0xp { name: "james", friend: 0xobj }
* 垃圾回收期
* 引用計數(shù)法
* 指向obj
* 指向一次 +1
* 少一次指向 -1
* 當(dāng)為0時, 就GC被回收
* 弊端
* 引用計數(shù)存在一個很大的弊端 : 循環(huán)引用
* 示例
* var obj1 = { friend: obj2 };
* var obj2 = { friend: obj1 };
* 標(biāo)記清除
* 看附帶圖片
*/
垃圾回收器
- 英文
- Garbage Collection,簡稱GC
- 對于那么不再使用的對象,我們都稱之為是垃圾,他需要被回收,已釋放更多的內(nèi)存空間
- 而我們的語言運行環(huán)境,比如Java的運行環(huán)境JVM,JavaScript的運行環(huán)境JS引擎都會內(nèi)存垃圾回收器
GC算法

垃圾回收機制-引用計數(shù).png

垃圾回收機制-標(biāo)記清除.png