This.作用域.闭包

ppk on javascript

JavaScript是个有点神奇的语言,不过它的一些独有的特性往往让我们初学者感到费解。ThisJavascript语言的一个关键词。不过它到底是指什么呢?很多人都会认为this指的是当前对象。当然,这样理解是没错的,但是在有些情况下仍然会有些问题。在此,我搜集了一些资料,重新学习并整理一下,希望能借此来更好的理解this在JS中的工作方式和使用方法。

  1. var test = function(){
  2.      alert(this);
  3. }
  4. test();
  5. new test();

运行以上代码,你会发现test()和new test()的运行结果是不一样的,test()指向的是Windows对象而new test()才是指向test对象,为什么会有两种不同的运行结果?其实这里就涉及到一个变量作用域的问题,而变量作用域同时又牵涉到闭包(Closure)这个JS特性了,正因为闭包的存在,理解变量作用域就显得非常重要。

关于变量作用域

接着先来介绍一下所谓的变量作用域,概念非常简单,每个变量都有自己的作用域,即变量在这么一个区域中可以被识别,而出了此区域就没有任何作用了。作用域就两种:全局作用域和局部作用域。
全局变量在JavaScript中处处都有定义,它贯穿在一个全局对象中,因此可以在任何地方使用。而局部变量则只在一个函数中有定义。其中要注意到的一点就是:JavaScript无块级作用域,这有别于C++和Java。因此任何变量在定义它的整个函数体中都能被识别。

  1. var a=123;
  2. function fun1(){
  3.     alert(a);
  4.   }
  5.  
  6. fun1(); // 123

很简单,函数内部可以直接读取全局变量;

  1. function fun2(){
  2.  var a=123;
  3. }
  4.  
  5. alert(a); // error

在函数外部当然无法读取函数内的局部变量。

  1. var s="oo";
  2.  
  3. function fun3(){
  4.  alert(s); // undefined
  5.  
  6.  var s ="xx";
  7.  
  8.  alert(s); // xx 因为javascript无块级作用域,s在此被初始化,整个函数中都有定义
  9. }
  10.  
  11. fun3();

以上代码其实就相当于

  1. function fun3(){
  2.  var s;
  3.  alert(s);
  4.  
  5.  s = "xx";
  6.  alert(s);
  7. }    
  8.  
  9. fun3();

另外大家要注意的一点就是,在函数内部声明变量的时候,如果不用var命令,这样其实就相当声明了一个全局变量。

关于闭包

了解过变量作用域之后,对于理解闭包就有很大的帮助了。当我们需要得到函数内的局部变量的时候,就需要在函数的内部再定义一个函数。

  1. function fun1(){
  2.  var a = 123;
  3.  function fun2(){
  4.   alert(a); //123
  5.  }
  6. }

fun2可以访问fun1所有的局部变量,因此我们只要把fun2作为返回值,就可以在fun1外部读取到它的内部变量了。

  1. function fun1(){
  2.  var a = 123;
  3.  function fun2(){
  4.   alert(a); //123
  5.  }
  6.  
  7.  return fun2;
  8. }
  9.  
  10. var result = fun2();
  11. result();

因此,有了闭包将联系函数内外联系起来,我们就可以从函数外读取到函数内部的变量了,另外由于JavaScript作用域的机制,闭包只能读取到包含函数中任何变量的最后一个值。
这里引用一个例子:

  1. function createFunctions(){
  2.  var result = new Array();
  3.  
  4.  for (var i=0; i < 10; i++){
  5.   result[i] = function(){
  6.    return i;
  7.   };
  8.  }
  9.  
  10.  return result;
  11. }
  12.  
  13. var funcs = createFunctions();
  14.  
  15. for(var i=0; i < funcs.length; i++){
  16.  alert(funcs[i]());  //每次都显示10
  17. }

因为每个函数作用域中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i,当createFunctions()函数返回后,变量i的值是10,因此每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部i的值都是10。我们要稍微改写一下函数,让它符合我们的预期要求。

  1. function createFunctions(){
  2.  var result = new Array();
  3.  
  4.  for (var i=0; i < 10; i++){
  5.   result[i] = function(num){
  6.     return num;
  7.   }(i);
  8.  }
  9.  
  10.  return result;
  11. }
  12.  
  13. var funcs = createFunctions();
  14.  
  15. for(var i=0; i < funcs.length; i++){
  16.  alert(funcs[i]);  //每次都显示10
  17. }

这里,我们通过定义一个匿名函数,并立即执行该匿名函数的结果赋给数组,这里的匿名函数只有一个参数num,也就是最终要返回的值。在调用每个匿名函数时,我们传入了变量i,通过变量i的将当前值赋给参数num,这样就获得我们所要的结果。

清楚了作用域和闭包之后,对于this就能很快理解了。先来看下面这段代码:

  1. function test1{
  2.  this.x = 123;
  3.  alert(this.x);
  4. }
  5.  
  6. test1(); //123

当前的this就是全局对象,稍作变化,效果一样。

  1. var x = 1;
  2.    function test(){
  3.      this.x = 0;
  4. }
  5.   alert(x); //1
  6.  
  7.   test();
  8.   alert(x); //0

再看如下代码:

  1. var oo = new Object;
  2.  
  3. oo.x = 1;
  4.  
  5. function test2(){
  6.  
  7.  alert(this.x);
  8.  
  9. }
  10.  
  11. oo.fun = test2;
  12.  
  13. oo.fun();  // 1

这里的this就指当前调用该方法的对象。
对于开头这段代码来说,test()函数是在全局作用域下的(在这里其实就是window对象),所以this的值是当前的window对象。而通过 new test()其实是作为构造函数来调用的,就是通过这个函数来生成一个新的对象,所以这里的this就指的是这个新对象。

  1. var xx = 2;
  2.  
  3. function test3(){
  4.  
  5.   this.xx = 1;
  6.  
  7. }
  8.  
  9. var oo = new test3();
  10. alert(oo.xx); //1
  11. alert(xx); //2

以上例子可证明this并不是全局对象,因为xx的值并没有改变。最后在网上看到两段例子,对于理解this和闭包有很大的帮助,在此引用过来。

代码一

  
  1. var name = "The Window";
  2.  
  3.   var object = {
  4.     name : "My Object",
  5.  
  6.     getNameFunc : function(){
  7.       return function(){
  8.         return this.name;
  9.       };
  10.  
  11.     }
  12.  
  13.   };
  14.  
  15.   alert(object.getNameFunc()());

代码二

  
  1. var name = "The Window";
  2.  
  3.   var object = {
  4.     name : "My Object",
  5.  
  6.     getNameFunc : function(){
  7.       var that = this;
  8.       return function(){
  9.         return that.name;
  10.       };
  11.  
  12.     }
  13.  
  14.   };
  15.  
  16.   alert(object.getNameFunc()());

由于本人水平有限,此文也算是我最近学习过程中的一个小结吧,若有不当之处,还望大家指正。

Post a comment