JavaScript的this和闭包

jopen 12年前

作为OO语言,c++、c#或者Java等语言,都有this的概念,JavaScript中存在this的概念,一般编程语言的this就是对象自己,而JavaScript的this却不一定!

在JavaScript中,this所引用的对象(很多书籍叫函数上下文,我也这样叫吧)并不是由声明函数的方式决定的,而是由调用函数的方式决定的,下面看代码:

var o1 = {handle : 'o1' } ;
var o2 = {handle : 'o2' } ;
var o3 = {handle : 'o3' } ;
window. handle = 'window' ;

function whoAmI ( ) {
return this. handle ;
}

o1. identifyMe = whoAmI ;

alert (whoAmI ( ) ) ; // 输出window
alert (o1. identifyMe ) ; // 输出o1
alert (whoAmI. call (o2 ) ) ; // 输出o2
alert (whoAmI. apply (o3 ) ) ; // 输出o3

以上输出说明了下面几个问题:

  • 当直接把函数作为顶层函数来调用时,函数上下文是window;
  • 当把函数作为对象的属性来调用时,该对象就成为函数调用的函数上下文;
  • 使用Function的call()方法把函数上下文设置为传入call()的第一个参数的任何对象,这时该传入对象成为函数调用的函数上下文;

顺便补充介绍下Function类的call()和apply()方法,它们都是用来调用函数的。
(1)call()方法

call(thisObject[, parameter])

参数thisObject是一个类,指定函数体内this关键字的值,该参数是必须的;
参数parameter是可选的,是要传递给函数的参数,可以指定0或多个参数(参数为用逗号分隔的列表)。

(2)apply()方法

apply(thisObject[, argArray])

与call不同的是,第二个参数是用数组模式来传递的。

下面是闭包的内容了(下面部分内容参考于网络)

简单说下什么是闭包:

  • 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在;
  • 闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配;
  • 当在一个函数内定义另外一个函数就会产生闭包;

上面的第二定义是第一个补充说明,抽取第一个定义的主谓宾——闭包是函数的“局部变量”集合。只是这个局部变量是可以在函数返回后被访问。(这个不是官方定义,但是这个定义应该更有利于你理解闭包)。

做为局部变量都可以被函数内的代码访问,这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象,因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是,ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中,函数对象中定义的内部函数(inner function)是可以直接访问外部函数的局部变量,通过这种机制,我们就可以以如下的方式完成对闭包的访问了。

请看以下代码:

function hello ( name ) {
var text = 'hello ' + name ;
return function ( ) { alert (text ) ; }
}
var sayHello = hello ( 'jack' ) ;
sayHello ( ) ; // 这里通过闭包访问到了hello()中的text参数

再看一段定时器代码

$ ( function ( ) {
var local = 1 ;
window. setInterval ( function ( ) {
alert (local ) ; // 这里通过闭包访问到了local变量
local ++;
} , 1000 ) ;
} ) ;

另外,闭包有一个重要的特征:函数上下文绝不会被包含为闭包的一部分。例如下面的代码不会按照我们期望的方式执行:

// ...
this. id = 'someID' ;
$ ( '*' ). each ( function ( ) {
alert ( this. id ) ;
} ) ;

每个函数调用都有其函数上下文(this指向的对象),因此上面的代码,传递给each()的函数上下文在回调函数内是来自于jQuery包装集的元素,而不是被设置为”someID”的外部函数属性。对回调函数的每个调用都会依次弹出警告框,用来显示包装集中各个元素的id。

如果需要访问在外部函数中作为函数上下文的对象,可以考虑通常的习惯用法:在局部变量中创建this引用的副本,这个副本将会被包含在闭包中。

// ...
this. id = "someID" ;
var _that = this ;
$ ( '*' ). each ( function ( ) {
alert (_that. id ) ;
} ) ;