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_function
time, the scope chain is as follows:
outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
window { global_scope: 'global' }
Therefore, the print outer_scope
time, 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_function
time, the scope chain is as follows:
inner_function { inner_params: 'inner', inner_scope: 'inner'}
outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
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 ]