JavaScript study notes (XI) Closures

What is closure? Let's take a look at the definition "JavaScript The Definitive Guide" in:

Function object may be associated by the scope chain up, the interior of the body of the function stored in the function can be variable scope, this feature is called closure

Ha ha ha look ignorant reading is not it? Does not matter, let's simplest scope, the scope chain to start, step by step to explore what is closure

1. Scope

(1) Function scope

What is the scope? A variable's scope is defined in the source of the variable region are employed in JavaScript function scope

In other words, in the declaration of their body functions and any functions within the body of the function are all defined nested

function scope() {
    if (true) {
        var i = 0
        for (var j = 1; j <= 5; j++) {
            i = i + j
        }
        console.log(j)
    }
    console.log(i)
    ~function() { // 立即执行函数
        console.log(i)
        console.log(j)
    }()
}

scope()

/*
 * 执行结果:
 * 6
 * 15
 * 15
 * 6
**/

(2) a statement in advance

JavaScript uses function scope, which means that all variables declared within a function in the body of the function are visible

This leads to variable declaration had been available before, in a phenomenon known as JavaScript in a statement in advance, but note that the assignment will not advance

function hoisting() {
    console.log(i) // 声明提前,但赋值并不会提前
    var i = 'hello'
    console.log(i)
}

hoisting()

/*
 * 执行结果:
 * undefined
 * hello
**/

2, the scope chain

(1) declare the context object

We know that global variables in JavaScript is a property of the global object, but many people do not know the local variables are properties of an object

This object is called the declaration context object, it is an object associated with the function call, implemented as an internal, we can not refer directly to the object

(2) Scope chain

Each piece of JavaScript code (global code or function) has an associated scope chain

The so-called scope chain is actually a list of objects, the set of objects defined variable scope of the code, called a variable object

In general, variables defined object comprises a global variable declarations define local variables and global objects context object

In the global code (the code is not included in the definition of the function), only one object on the scope chain, is the global object

For the nested function is not included, there are two objects on the scope chain, a first object function arguments and local variables, the second is the global object

Contains a nested function for the presence of its scope chain of at least three objects (think about what objects are three)

(3) variable parsing

When JavaScript needs to find the value of a variable x, it will search from the first object on the scope chain

If there is this property of the object named x, it is used directly as the value of the property value of the variable x

If the object does not have a property named x, you will find the next object on the scope chain until it reaches the final scope chain

If there is no existence of an object property named x on the scope chain, then throw a reference exception

(4) Function scope chain

When defining a function, it will save a scope chain; when this function is called, it will create a new object for local variables

Then add the object to the saved scope chain, and create a new scope chain indicates that the scope of function calls

When the function returns, the object is deleted from the saved scope chain, but this does not mean that the object is garbage collected immediately

Because, according to the JavaScript garbage collection mechanism, only when the object is not referenced, JavaScript will be recovered

3, closure

Understood that (1) the closure

If the nested function comprising an internal function (but does not return the nested function), then the external function returns, the nested function also recovered

The core function of the closure that comprises a nested function, and returns a nested function, then there is a reference point to the outside of the nested function

The nested function will not be garbage collected, it corresponds to the scope chain is also preserved

var global_scope = 'global'
function outer_function(outer_params) {
    var outer_scope = 'outer'
    console.log(outer_scope)
    // console.log(inner_scope) -> inner_scope is not defined
    var inner_function = function(inner_params) {
        var inner_scope = 'inner'
        console.log(outer_scope)
        console.log(inner_scope)
    }
    return inner_function
}
outer_function('outer')('inner')

/*
 * 执行结果:
 * outer
 * outer
 * inner
**/

In the call outer_functiontime, the scope chain is as follows:

  1. outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
  2. window { global_scope: 'global' }

Therefore, the print outer_scopetime, first look outer_function objects, which can be found outer_scope property, direct return

If you want to print inner_scope, find outer_function objects and window objects are not found outer_scope property, an exception is thrown


In the call inner_functiontime, the scope chain is as follows:

  1. inner_function { inner_params: 'inner', inner_scope: 'inner'}
  2. outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
  3. window { global_scope: 'global', outer_function: function }

In print outer_scope, the first looks inner_function object is not found, then look outer_function objects to find

In print inner_scope, the first looks inner_function objects to find

Application (2) closure

  • The definition of private property
var counter = (function() { // 立即执行函数,返回一个对象
    var value = 0 // 私有属性,无法直接访问
    var changeBy = function(val) { value += val } // 私有方法,无法直接访问
    // 以下多个嵌套函数共享一个作用域链
    return {
        getValue: function() { return value },
        increase: function() { changeBy(+1) },
        decrease: function() { changeBy(-1) }
    }
})()

console.log(counter.getValue())
counter.increase()
console.log(counter.getValue())
counter.decrease()
console.log(counter.getValue())

/*
 * 执行结果:
 * 0
 * 1
 * 0
**/
  • Caching results
function memory(f) {
    // 缓存处理结果
    var cache = {}
    return function() {
        // 将传入的参数作为键
        var key = arguments.length + Array.prototype.join.call(arguments, ',')
        if (key in cache) { // 如果值在缓存,直接读取返回
            return cache[key]
        } else { // 否则执行计算,并把结果放到缓存
            return cache[key] = f.apply(this, arguments)
        }
    }
}

var factorial = function(n) { return (n <= 1) ? 1 : n * factorial(n - 1) }

var factorialWithMemory = memory(factorial)

(3) Note the use of closures

  • Whether to change the value of the external variables
function test() {
    var array = []
    for(var count = 0; count < 5; count++) {
        array[count] = function() { console.log(count) }
    }
    return array
}

var result = test()
result[0]()
result[1]()
result[2]()
result[3]()
result[4]()

/*
 * 执行结果:
 * 5
 * 5
 * 5
 * 5
 * 5
**/

/*
 * 结果分析:
 * 在调用数组中的函数时,由于需要打印变量 count 的值,所以沿着这些函数的作用域链往上查找
 * 最终在外层函数 test 的变量对象中找到,但是此时局部变量 count 的值已经变成 5
**/

Solution

function test() {
    var array = []
    for(var count = 0; count < 5; count++) {
        array[count] = function(value) { // 立即执行函数
            return function() { console.log(value) }
        }(count)
    }
    return array
}

var result = test()
result[0]()
result[1]()
result[2]()
result[3]()
result[4]()

/*
 * 执行结果:
 * 0
 * 1
 * 2
 * 3
 * 4
**/

/*
 * 结果分析:
 * 同样道理,由于需要打印变量 value 的值,所以在调用数组中的函数时需要往上查找作用域链
 * 但是此时在上层函数对应的变量对象中即可找到 value,并且 value 的值等于当时传入的 count 的值
**/
  • this point whether or not in line with expectations
var test = {
    value: 0,
    getValue: function() {
        return function() { console.log(this.value) }
    }
}

var result = test.getValue()
result()

/*
 * 执行结果:
 * undefined
**/

/*
 * 结果分析:
 * 在调用闭包函数时,是在全局作用域的环境下执行的,因此 this 指向全局对象
**/

Solution

var test = {
    value: 0,
    getValue: function() {
        return function() { console.log(this.value) }.bind(this)
    }
}

var result = test.getValue()
result()

/*
 * 执行结果:
 * 0
**/

/*
 * 结果分析:
 * 使用 bind 函数将 this 的值绑定为 test 对象
**/

[Read More JavaScript series of articles, look at JavaScript study notes ]

Guess you like

Origin www.cnblogs.com/wsmrzx/p/12194629.html