javascript 研究闭包的工作原理

闭包可以访问创建函数时所在作用域内的全部变量,闭包有用的方法:通过闭包模拟私有变量,通过回调函数使得代码更加优雅。闭包与作用域密切相关。闭包对JavaScript的作用域规则产生了直接影响。

使用闭包模拟私有变量的代码

console.log("------使用闭包模拟私有变量-------");
//在构造函数内部声明变量。由于该变量的作用域在构造函数内部,因此,feints是一个私有变量
function NinjaTest() {
  var feints = 0;
  //访问feints计划的方法
  this.getFeints = function () {
    return feints;
  }
  //变量feints的增值方法。由于feints是私有变量,因此,无法通过其他方法改变变量feints的值。
  this.feint = function () {
    feints++;
  }
}

var ninjaTest1 = new NinjaTest();
//验证我们无法直接访问变量feints
if (ninjaTest1.feints === undefined) {
  console.log("And the private data is inaccessible to us.");
}
//调用feints()方法,增加变量feints的值进行统计调用次数
ninjaTest1.feint();
//验证已经执行了递增
if (ninjaTest1.getFeints() === 1) {
  console.log("We are able to access the internal feint count.");
}

var ninjaTest2 = new NinjaTest();
//当通过构造函数创建ninjaTest2时,实例ninjaTest2具有独立的和变量feints.
if (ninjaTest2.getFeints() === 0) {
  console.log("The second ninjaTest object gets its own feints variable.");
}

 

我们分析第一个NinjTest对象创建完成之后的程序的状态,我们可以利用标识符原理来更好理解这种情况之下闭包的工作原理。通过关键字new调用JavaScript构造函数。因此,每次调用构造函数时,都会创建一个新的词法环境,该词法环境保存构造函数内部的局部变量。在上述代码中,创建了NinjaTest环境,保存对变量feints的跟踪。

此时,无论何时创建函数,都会保持对词法环境的引用(通过内置[[Enviroment]]属性)。在上述代码中,NinjaTest构造函数内部,我们创建了两个函数:getFeints和feint,均有NijaTest环境的引用,因为NinjaTest环境是这两个函数创建时所处的环境。getFeints与feint函数是新创建的ninjaTest的对象方法(可以通过this关键字访问)。因此,可以通过NinjaTest构造函数外部访问getFeints与feint函数,这样实际上就创建了包含feints变量的闭包。

如上图所示,当再次创建一个NinjaTest实例时,将重复整个过程。如上图所示,显示了创建了第二个NinjaTest对象之后的程序状态。

每一个通过NinjaTest构造函数创建的对象实例获得了各自的方法(ninjaTest1.getFeints()和ninjaTest2.getFeints()),当调用构造函数时,各自的实例方法包含各自的变量。这些“私有变量”只能通过构造函数内定义的对象方法进行访问,不允许直接访问。

ninjaTest2.getFeints方法调用时发生了什么?如下如所示:

在调用ninjaTest2.getFeints()时,执行环境与词法环境的状态。创建新的getFeints环境,该环境具有构造函数创建ninjaTest2对象时所在的环境。getFeints函数可以访问“私有”feints变量。

 

 

 

在调用ninjaTest2.getFeints方法之前,JavaScript引擎正在执行全局代码。我们的程序处于全局执行上下文状态,是执行栈里的唯一上下文。同时,唯一活跃的词法环境是全局环境,与全局执行上下文关联。

 

当调用ninjaTest2.getFeints()时,我们调用的是ninjaTest2对象的getFeints方法。由于每次调用函数时均会创建新的执行上下文,因此创建了新的getFeints执行环境并推入执行栈。

这同时引起创建新的词法环境,词法环境通常用于保持跟踪函数中定义的变量。另外,getFeints词法环境包含了getFeints函数被创建时所处的环境,当ninjaTest2对象创建时,NijaTest环境是活跃的。

 

现在我们了解了视图获取feints变量时是如何工作的。首先,访问活跃的getFeints词法环境。因为在getFeints函数内部未定义任何变量,该词法环境是空的,找不到feints变量。接下来,在当前词法环境的外部环境进行查找————在上述代码中,当创建ninjaTest2对象时,NinjaTest环境处于活跃状态。Ninja环境中具有feints变量的引用,完成搜索过程。

 

通过分析,我们理解了处理闭包时,执行上下文与词法环境所扮演的角色,那么接下来,我们将关注“私有”变量,为什么要保持“私有”变量的引用。这下“私有”变量并不是对象的私有属性,但是可以通过构造函数所创建的对象方法去访问这些变量。

 

参考《JavaScript忍者秘籍》

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/85297796