JavaScript Memory & Scope & Closures

1. Execution context and scope

The execution context is called "context" for short. The context of variables and functions determines which data they can access and their behavior. Each context has a variable object VO (variable object) , and the variables defined in this context will be stored in this object.

The global context is the outermost context. The objects stored in the variables and functions defined in it are called the global object GO (global object) , which is what we often call the window object . The functions and variables defined here will become the methods and methods of the window object. Attributes.

var name = "张三"; // 全局属性
var age = 18;
var gender = "男";
var person = { 
    name: "李四",
    age: 20
}
// 全局方法
var foo = function() {
    console.log("你好");
}

When the function is executed, an independent execution context will be created and put into the global context (execution stack). When the function is executed, its execution context will be removed from the global execution context, and its internal variables and functions will also be destroyed.

When the code in the context is executed, a scope chain (scope chain) of variable objects will be created , and the scope chain determines the order in which the contexts at all levels read variables and functions.

var name = "张三"; // 全局属性
var person = { // 全局执行上下文的一个对象,保存的是一个地址
    name: "李四", // 在内存中开辟空间去存放
}
// 全局方法
var foo = function() {
    var hobby = "篮球"; // 函数局部属性
    console.log("你好");
    console.log(name); // 张三,沿着作用域链往上找到全局中的 name,而不是 person 对象中的
    console.log("你好");
    console.log(person.name); // 李四
}
foo()
console.log(hobby); // undefined,只能在全局找,找不到就 undefined,不能往下找
console.log(name); // "张三", 此时只能访问全局的变量

After the code of the context is executed, the context is destroyed , including the variables and methods defined in it. The global context is only destroyed when the program exits or the browser is closed. 

// bar 会被销毁
var foo = function() {
    var bar = function() {
        var role = "玄武";
        console.log(role); // "玄武"
    };
    return bar; 
};
foo()
console.log(bar); // undefined, foo 执行完被销毁

2. Garbage collection

JavaScript is a language that uses garbage collection. For unnecessary variables and objects, the memory occupied by them should be released. Not all the time it is so easy to find out which ones are not used anymore, there are two ways how to mark unused variables: mark cleaning, reference counting.

Mark cleanup: When a variable enters the context, such as declaring a variable inside a function, the variable will be marked to exist in the context, but it does not exist and should not be released, because no one can guarantee whether it will be used. When the variable leaves the context, Markup that will be flagged out of context. There are many ways to mark variables, the key is strategy. When the specified time interval has passed, the garbage collector will destroy the variables marked no longer used and reclaim their memory.

Reference counting: The idea is to mark the number of times each value has been referenced. If it is referenced, it will be +1, and if it is referenced by another variable, it will also be +1. If the variable that references it is overwritten (destroyed), then the reference count -1 , when the specified time interval has elapsed, the garbage collector will destroy the variable with a reference number of 0 and reclaim its memory.

Disadvantages of reference counting: the number of marks for the following object1 and object2 is 2, and will not be cleaned up.

And it is no problem for the following mark clear, when foo is executed, both object1 and object2 will be marked as not existing in the context.

function foo() {
    var object1 = {
        name: "object1",
        obj: object2,
    };
    var object2 = {
        name: "object2",
        obj: object1,
    };
    console.log(object1); // {name: 'object1', obj: undefined}
    console.log(object2); // {name: 'object2', obj: {name:object1,obj:undefined}}
}
foo()
function koo() {
    function foo() {
        bar() 
    }
    
    function bar() {
        foo()
    }
    foo() // Maximum call stack size exceeded 栈溢出
}

koo() // 对于标记清理,按理此时 koo 里面的都会被标记为不存在于上下文,
      // 可是函数内部存在循环调用,执行栈溢出。。。

3. Memory leak

JavaScript memory leaks are mostly caused by unreasonable definitions and reference variables. Big problem with limited memory.

function foo() {
    name = "张三";
    console.log(name); // "张三"
}
foo()

console.log(name); // "张三"
console.log(window.name); // "张三"

function bar() {
    var name = "张三";
    console.log(name); // "张三"
}
bar()

console.log(name); // ""
console.log(window.name); // ""

 At this time, name is defined inside the foo function, but if var, let, and const are not used, it will be defined globally (window).

The timer causes a memory leak: the callback function refers to the variable age of the outer function foo, resulting in a memory leak.

// 定时器内存泄露

function foo() {
    var age = 18; // 被引用,计时器不结束就不销毁
    setInterval(()=>{
        console.log(age);
    },1000)
}

foo()

4. Closures

Closures, also known as lexical closures (Lexical Closures) and function closures (function closures), are techniques for implementing lexical bindings in programming languages ​​that support first-class functions (functions can be passed as parameters). A closure is like a structure that stores a function (usually an address) and its associated environment. When the closure is captured, its free variables (internal references defined outside the function) will be determined at the time of capture, so that even if it breaks away from the context of capture, it can still run as usual, which will cause a memory leak.

The following is a closure:

var foo = function() {
    var age = 18;
    var boo = function() {
        console.log(age);
    }
    return boo
}

var bar = foo() // 全局变量 bar 引用了  age

Memory map: 

// 闭包 1
var foo = function() {
    var age = 18;
    var boo = function() {
        console.log(age);
        age++; // 此时对 age + 1,
    }
    return boo
}

var bar = foo() // 全局变量 bar 引用了  age

bar() // 执行完不清空执行上下文 , age 不会被清除 // 18
bar() // 又因为上次 age 被 + 1,这次输出 19
// 闭包 2
function aoo() {
    var a = 18;
    return function() {
        return ++a
    }
}

console.log(aoo()()); // 此时并没有在全局接受,执行完清空上下文 //19
console.log(aoo()()); // 第二次执行之前已经清空了执行上下文 //19
console.log(aoo()()); // 19
// 闭包 3
function coo() {
    var b = 18;
    return function() {
        return --b; // 此时 -1
    }
}

var bqq = coo();

console.log(bqq()); // 17
console.log(bqq()); // 16
console.log(bqq()); // 15

Guess you like

Origin blog.csdn.net/m0_66492535/article/details/127900167