日期:2014-05-16  浏览次数:20347 次

谈JS中匿名函数的上下文环境

???? 受一位热心网友的关注和提问,我又对JS中的匿名函数的上下文环境作了详细地分析和实验,下面把我对该问题的一点思考与大家分享:

?

??? 关键字 this????


??? 在JavaScript的对象系统中,使用关键字 this 的地方:在对象的方法(或属性)被调用时,指代调用该方法(或属性)的对象实例。举个例子:
???

function foo(){
	this.property = "red";
	this.method = function(){...}
}

?

?

??? 使用 this 的原因?

??? 因为在实例化对象时,总是不能确定开发者会使用什么样的变量名。使用 this,即可在任何多个地方重用同一个函数。请思考下面的例子:

???

function showColor() {
	alert(this.color);
};

var oCar1 = new Object;
oCar1.color = "red";
oCar1.showColor = showColor;

var oCar2 = new Object;
oCar2.color = "blue";
oCar2.showColor = showColor;

oCar1.showColor();		//输出 "red"
oCar2.showColor();		//输出 "blue"

?
??? 注意,引用对象的属性时,必须使用 this 关键字。例如,如果采用下面的代码,showColor() 方法不能运行:

?

???

function showColor() {
	alert(color);
};

?

?

??? 如果不用对象或 this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量。然后该函数将查找名为 color 的局部或全局变量,但是不会找到。结果如何呢?该函数将在警告中显示 "null"。

?

??? 函数与方法??

??? 由于在JavaScript中不明确区分函数与方法。因此有些代码看起来很奇怪:

???

function foo() {
	// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("call a function.", "<br />");
	}
	else {
		document.write("call a method, by object: ", this.name, "<br />");
	}
}

function MyObject(name) {
	// 下面的this指代new关键字新创建实例
	this.name = name;
	this.foo = foo;
}

var obj1 = new MyObject("obj1");
var obj2 = new MyObject("obj2");

// 测试1:	作为函数调用
foo();		//Output=>call a function.

// 测试2:	作为对象方法的调用
obj1.foo();	//Output=>call a method, by object: obj1
obj2.foo();	//Output=>call a method, by object: obj2

// 测试3:	将函数作为“指定对象的”方法调用
foo.call(obj1);	//Output=>call a method, by object: obj1
foo.apply(obj2);	//Output=>call a method, by object: obj2

?

?

??? 在测试2里,obj1/obj2对foo()的调用是很普通的调用方法。
??? 而测试3中的call()与apply()就比较特殊。在这个测试中,foo()仍然作为普通函数来调用,只是JavaScript的语言特性允许在call()/apply()时,传入一个对象实例来指定foo()的上下文环境中所出现的this关键字的引用。需要注意的是,此时的foo()仍旧是一个普通函数调用,而不是对象方法调用,该测试只是将foo函数的this原来引用‘window’更改为后来参数对象,不要被测试三的结果迷惑了,不信看下面例子:

???

function foo() {
	// 下面的this指代调用该方法的对象实例
	if (this===window) {
		document.write("call a function.", "<br	/>");
	}
	else{
		document.write("call a function or method.", "<br />");
	}
}

function MyObject(name) {
	// 下面的this指代new关键字新创建实例
	this.name = name;
	this.foo = function(){
		document.write("call a method, by object: ", this.name, " ; and then ");
		foo();
	};
}

var obj1 = new MyObject("obj1");
var obj2 = new MyObject("obj2");

// 测试1: 作为函数调用
foo();		//Output=>call a function.

// 测试2: 作为对象方法的调用
obj1.foo();	//Output=>call a method, by object: obj1 ; and then call a function.
obj2.foo();	//Output=>call a method, by object: obj2 ; and then call a function.

// 测试3: 将函数作为“指定对象的”方法调用
foo.call(obj1);	//Output=>call a function or method.
foo.apply(obj2);	//Output=>call a function or method.

?

?

??? 测试3结果中明显不包含字符串“call a method, by object:”,说明该调用明显不是method,而是function。

??? 但再仔细想想,严格来说,全局foo()函数也是方法,它其实是其上下文环境(wi