一、提要
关于闭包的定义(红宝书P178):闭包就是指有权接见别的一个函数的作用域中的变量的函数。
症结点:
1、闭包是一个函数
2、能够接见别的一个函数作用域中的变量
二、闭包特征
关于闭包有下面三个特征:
1、闭包能够接见当前函数之外的变量
function getOuter(){
var date = ‘815’;
function getDate(str){
console.log(str + date); //接见外部的date
}
return getDate(‘今天是:’); //”今天是:815″
}
getOuter();
2、纵然外部函数已返回,闭包仍能接见外部函数定义的变量
function getOuter(){
var date = ‘815’;
function getDate(str){
console.log(str + date); //接见外部的date
}
return getDate; //外部函数返回
}
var today = getOuter();
today(‘今天是:’); //”今天是:815″
today(‘来日诰日不是:’); //”来日诰日不是:815″
3、闭包能够更新外部变量的值
function updateCount(){
var count = 0;
function getCount(val){
count = val;
console.log(count);
}
return getCount; //外部函数返回
}
var count = updateCount();
count(815); //815
count(816); //816
三、作用域链
javascript中有一个实行上下文(execution context)的观点,它定义了变量或函数有权接见的其他数据,决议它们各自的行动。每个实行环境都有一个与之关联的变量对象,环境中定义的一切变量和函数都保留在这个对象中。你能够把它当作Javascript的一个一般对象,然则你只能修正它的属性,却不能援用它。
变量对象也是有父作用域的。
作用域链定义:当接见一个变量时,诠释器会首先在当前作用域查找标示符,假如没有找到,就去父作用域找,直到找到该变量的标示符或许不再存在父作用域了,这就是作用域链。
作用域链和原型继续有点相似,但又有点小区分:假如去查找一个一般对象的属性时,在当前对象和其原型中都找不到时,会返回undefined;但查找的属性在作用域链中不存在的话就会抛出ReferenceError。
作用域链的顶端是全局对象。关于全局环境中的代码,作用域链只包括一个元素:全局对象。所以,在全局环境中定义变量的时刻,它们就会被定义到全局对象中。当函数被挪用的时刻,作用域链就会包括多个作用域对象。
四、全局环境
关于作用域链讲得略多(红皮书上有关于作用域及实行环境的细致诠释),看一个简朴地例子:
// my_script.js
“use strict”;
var foo = 1;
var bar = 2;
在全局环境中,建立了两个简朴地变量。如前面所说,此时变量对象是全局对象:
实行上述代码,my_script.js自身会构成一个实行环境,以及它所援用的变量对象。
4.1无嵌套函数
// my_script.js
“use strict”;
var foo = 1;
var bar = 2;
function myFunc() {
var a = 1;
var b = 2;
var foo = 3;
console.log(“inside myFunc”);
}
console.log(“outside”);
myFunc();
定义时:当myFunc被定义的时刻,myFunc的标识符(identifier)就被加到了全局对象中,这个标识符所援用的是一个函数对象(myFunc function object)。
内部属性[[scope]]指向当前的作用域对象,也就是函数的标识符被建立的时刻,我们所能够直接接见的谁人作用域对象(即全局对象)。
myFunc所援用的函数对象,其自身不单单议含有函数的代码,而且还含有指向其被建立的时刻的作用域对象。
挪用时:当myFunc函数被挪用的时刻,一个新的作用域对象被建立了。新的作用域对象中包括myFunc函数所定义的当地变量,以及其参数(arguments)。这个新的作用域对象的父作用域对象就是在运转myFunc时能直接接见的谁人作用域对象(即全局对象)。
4.2嵌套函数
当函数返回没有被援用的时刻,就会被渣滓接纳器接纳。然则关于闭包,纵然外部函数返回了,函数对象仍会援用它被建立时的作用域对象。
“use strict”;
function createCounter(initial) {
var counter = initial;
function increment(value) {
counter += value;
}
function get() {
return counter;
}
return {
increment: increment,
get: get
};
}
var myCounter = createCounter(100);
console.log(myCounter.get()); // 返回 100
myCounter.increment(5);
console.log(myCounter.get()); // 返回 105
当挪用 createCounter(100) 时,内嵌函数increment和get都有指向createCounter(100) scope的援用。假定createCounter(100)没有任何返回值,那末createCounter(100) scope不再被援用,因而就能够被渣滓接纳。
然则createCounter(100)实际上是有返回值的,而且返回值被存储在了myCounter中,所以对象之间的援用关联以下图:
纵然createCounter(100)已返回,然则其作用域仍在,而且只能被内联函数接见。能够经由过程挪用myCounter.increment() 或 myCounter.get()来直接接见createCounter(100)的作用域。
当myCounter.increment() 或 myCounter.get()被挪用时,新的作用域对象会被建立,而且该作用域对象的父作用域对象会是当前能够直接接见的作用域对象。
挪用get()时,当实行到return counter时,在get()地点的作用域并没有找到对应的标示符,就会沿着作用域链往上找,直到找到变量counter,然后返回该变量。
零丁挪用increment(5)时,参数value保留在当前的作用域对象。当函数要接见counter时,没有找到,因而沿着作用域链向上查找,在createCounter(100)的作用域找到了对应的标示符,increment()就会修正counter的值。除此之外,没有其他体式格局来修正这个变量。闭包的壮大也在于此,能够存贮私有数据。
建立两个函数:myCounter1和myCounter2
//my_script.js
“use strict”;
function createCounter(initial) {
/ … see the code from previous example … /
}
//– create counter objects
var myCounter1 = createCounter(100);
var myCounter2 = createCounter(200);
关联图以下
myCounter1.increment和myCounter2.increment的函数对象拥有着一样的代码以及一样的属性值(name,length等等),然则它们的[[scope]]指向的是不一样的作用域对象。