クロージャとは何ですか?レッツは、定義「JavaScriptのDefinitive Guideの」中を見てみましょう:
関数オブジェクトは、関数に格納されている機能の本体の内部は可変範囲とすることができる、スコープチェーンアップによって関連付けることができ、この機能はクロージャと呼ばれています
ハッハッハ無知な読書はそれがされていないに見えますか?重要ではありません、起動するのletの最も簡単なスコープ、スコープチェーン、閉鎖が何であるかを探索するためのステップバイステップ
1.適用範囲
(1)関数スコープ
スコープとは何ですか?変数のスコープは、JavaScript関数の範囲で使用される可変領域のソースに定義されています
換言すれば、彼らの身体機能と機能の身体内の任意の関数の宣言内のすべてのネストされた定義されています
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)事前に声明
JavaScriptは、関数スコープを使用して、すべての変数は、関数の本体に関数内で宣言されたことを意味するが可視であります
変数の宣言にこのリードは、事前に文ではJavaScriptとして知られている現象で、以前に利用されますが、ノート割り当てが事前ないことをしていました
function hoisting() {
console.log(i) // 声明提前,但赋值并不会提前
var i = 'hello'
console.log(i)
}
hoisting()
/*
* 执行结果:
* undefined
* hello
**/
図2に示すように、スコープチェーン
(1)コンテキストオブジェクトを宣言する
私たちは、JavaScriptでグローバル変数はグローバルオブジェクトのプロパティであることを知っているが、多くの人々は、ローカル変数は、オブジェクトのプロパティであるかわかりません
このオブジェクトは、それは我々がオブジェクトを直接参照することはできません、内部として実装され、関数呼び出しに関連付けられたオブジェクトである、宣言コンテキストオブジェクトと呼ばれています
(2)スコープチェーン
JavaScriptコード(グローバルコード又は機能)の各部分は、関連するスコープ鎖を有します
いわゆるスコープチェーンは、実際に可変オブジェクトと呼ばれるオブジェクトのリスト、コードの可変範囲を定義されたオブジェクトの集合であります
一般に、変数定義されたオブジェクトは、グローバル変数の宣言は、ローカル変数およびグローバルオブジェクトコンテキストオブジェクトを定義備えます
グローバルコード(コードは、関数の定義に含まれていない)においては、スコープチェーン上の唯一の目的は、グローバルオブジェクトであります
ネストされた関数が含まれていないため、スコープチェーン上の2つのオブジェクトがあり、第一の目的関数の引数とローカル変数は、第二は、グローバルオブジェクトであります
少なくとも3つのオブジェクトのそのスコープチェーンの存在のためにネストされた関数が含まれています(オブジェクトが3つあるかについて考えます)
(3)変数解析
JavaScriptは、変数xの値を見つけるために必要がある場合は、スコープチェーン上の最初のオブジェクトから検索します
Xという名前のオブジェクトのこのプロパティが存在する場合、それは変数xのプロパティ値の値として直接使用されます
オブジェクトは、xという名前のプロパティを持っていない場合、それは、最終的なスコープチェーンに到達するまで、あなたはスコープチェーン上の次のオブジェクトを検索します
スコープチェーン上のxという名前のオブジェクトのプロパティのない存在が存在しない場合は、参照例外をスロー
(4)関数スコープチェーン
関数を定義する場合は、スコープチェーンを保存します。この関数が呼び出されたとき、それはローカル変数のための新しいオブジェクトを作成します。
そして、保存されたスコープチェーンにオブジェクトを追加し、新しいスコープチェーンを作成して示し、その関数呼び出しのスコープ
関数が戻るとき、オブジェクトが保存されたスコープチェーンから削除されますが、これはオブジェクトがガベージ即座に収集されていることを意味するものではありません
オブジェクトが参照されていない場合ので、JavaScriptのガベージコレクションのメカニズムによると、唯一、JavaScriptが回収されます
3、閉鎖
その(1)の閉鎖を理解
ネストされた関数は、内部機能を含む(ただし、ネストされた関数を返さない)場合には、外部関数戻り、ネストされた関数は、回収します
ネストされた関数を備え、ネストされた関数を返すクロージャの中核機能は、ネストされた関数の外部に基準点があります
ネストされた関数はガベージコレクションされません、それはスコープチェーンに対応し、また保存され
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
**/
コールではouter_function
、次のように、時間、スコープチェーンは、次のとおりです。
outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
window { global_scope: 'global' }
そのため、印刷outer_scope
outer_scopeプロパティを見つけることができる時間、初見のouter_functionオブジェクト、ダイレクトリターン
印刷したい場合はinner_scope
見つからないouter_scopeプロパティを、outer_functionオブジェクトとウィンドウオブジェクトを見つけ、例外がスローされます
コールではinner_function
、次のように、時間、スコープチェーンは、次のとおりです。
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 }
印刷ではouter_scope
、最初のルックスのinner_functionオブジェクトが見つからない場合は、検索しouter_functionオブジェクトを見て
印刷ではinner_scope
、最初のルックスのinner_functionは見つけるためにオブジェクト
アプリケーション(2)クロージャ
- 私有財産の定義
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
**/
- キャッシュ結果
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)クロージャの使用に注意してください
- 外部変数の値を変更するかどうか
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
**/
ソリューション
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 的值
**/
- かどうかの期待に沿って、この時点
var test = {
value: 0,
getValue: function() {
return function() { console.log(this.value) }
}
}
var result = test.getValue()
result()
/*
* 执行结果:
* undefined
**/
/*
* 结果分析:
* 在调用闭包函数时,是在全局作用域的环境下执行的,因此 this 指向全局对象
**/
ソリューション
var test = {
value: 0,
getValue: function() {
return function() { console.log(this.value) }.bind(this)
}
}
var result = test.getValue()
result()
/*
* 执行结果:
* 0
**/
/*
* 结果分析:
* 使用 bind 函数将 this 的值绑定为 test 对象
**/
[記事の続きを読むJavaScriptのシリーズ、見JavaScriptの研究ノート ]