大多数程序员都认为this是在当前脚本或对象的作用域中引用一个普通元素的标识符。然后,this在JavaScript中是依赖于它的执行环境的一个关键字。请看下面的例子:
var sound = 'Roar!'; function myOrneryBeast() { this.style.color='green'; alert(sound); }
其中的this关键字包含在一个函数中,这个函数是某个对象的方法,在这里this引用的就是那个对象。我们知道在JavaScript中全部脚本都包含在window全局对象中。因此,在脚本的最顶层,this引用的就是window对象。但是,在myOrneryBeast()函数的环境中的this是如何解析为window对象的呢?因为myOrneryBeast()函数作为window对象的一个方法而存在的,这样函数中的this关键字将会解析为包含它的函数作为方法被调用时所属的对象。因为myOrneryBeast()函数是在window环境中被调用的,所以this.style.color将会引用window.style.color。相应的解析过程如下图所示:
不过,this的环境可以随着函数被赋值给不同的对象而改变。比如直接或者使用ADS.addEvent()方法将函数赋值给一个事件侦听器属性:
var sound = 'Roar!'; function myOrneryBeast() { this.style.color='green'; alert(sound); } function initPage() { var example = ADS.$('example'); // 使用事件属性方法 example.onclick = myOrneryBeast; // 或者使用ADS.addEvent()方法 ADS.addEvent(example,'click', myOrneryBeast); } ADS.addEvent(window,'load',initPage);
无论使用哪种事件注册模型,相应的元素在单击时会变成绿色,也会看到显示“Roar!”的警告框。如果对变量sound求值的结果仍然是“Roar!”,那么this不也仍然应该引用window对象吗?事实并不像期望的那样。因为JavaScript会事件侦听器被调用的环境中将this作为一个关键字来解析。在本例中,无论是onClick属性还是click事件,都是example对象的一个方法。因此,this会被解析为将函数作为其方法的对象,即将事件作为方法的HTML元素——this引用的是example这个HTML元素。
对于Sound变量而言,它在initPage()函数的作用域中没有改变,所以它在作用域链中始终会被解析为“Roar!”。解析过程如下图所示:
通过call()和apply()重新定义执行环境
关于this关键字的引用问题还有个问题需要解决,这个问题涉及环境的改变。如果改变环境并非是我们希望的那样,又该如何呢?下面看一个例子:
function doubleCheck() { this.message = 'Are you sure you want to leave?'; } doubleCheck.prototype.sayGoodbye = function() { return confirm(this.message); } initPage() { var clickedLink = new doubleCheck(); var links = document.getElementsByTagName('a'); for (var i=0 ; i<links.length ; i++) { ADS.addEvent(links[i], 'click', clickedLink.sayGoodbye); } } ADS.addEvent(window,'load',initPage); [/code] 这个例子循环遍历页面中的所有链接,然后将clickedLink.sayGoodbye()方法指定为每个链接的click事件侦听器。根据上面的例子我们知道,当sayGoodbye()方法在<a>这个HTML元素的环境中执行时,this引用的就是这个HTML元素,而不是clickedLink。那如何解决这个问题呢?可以使用Function对象的call()或apply()方法。这两个方法可以指定函数的执行环境。例如想让方法在window对象的环境中执行,可以使用: [code lang="js"] clickedLink.sayGoodbye.call(window); [/code] 或者: [code lang="js"] clickedLink.sayGoodbye.apply(window); [/code] 在本例中clickedLink.sayGoodbye()方法没有参数,如果方法有参数,则对于call()方法而言,是将参数列在对象之后: [code lang="js"] functionReference.call(object, argument1, argument2, ...) [/code] 对于apply(),则应该将参数作为一个数组: [code lang="js"] functionReference.apply(object, arguments) [/code] 参数的不同传递方式是call()和apply()之间唯一的区别。 为了调整方法的执行环境apply()方法特别有用。下面的bindFunction()方法返回的函数,会取得通过func参数传递进来的函数,然后将其应用到由obj参数传递进来的对象上面: [code lang="js"] function bindFunction(obj, func) { return function() { func.apply(obj,arguments); }; } [/code] 这个函数的主要目的是给原始的函数(func)创建一个新的执行环境。如下图所示: <a href="http://www.everyinch.net/wp-content/uploads/2014/01/blog_this3.jpg"><img src="http://www.everyinch.net/wp-content/uploads/2014/01/blog_this3.jpg" alt="blog_this3" width="450" height="425" class="alignnone size-full wp-image-4533" /></a> 下面将bindFunction函数加入到ADS库中: /** * 将func函数绑定到obj对象上 */ function bindFunction(obj, func) { return function() { func.apply(obj,arguments); }; }; window['ADS']['bindFunction'] = bindFunction;
使用这个方法可以修改任何方法的环境。具体到doubleCheck()的例子中,可以将原来的代码修改如下:
function doubleCheck() { 转载请注明:陈童的博客 » ADS2.3 JavaScript中的对象——关于this
this.message = 'Are you sure you want to leave?';
}
doubleCheck.prototype.sayGoodbye = function() {
return confirm(this.message);
}
initPage() {
var clickedLink = new doubleCheck();
var links = document.getElementsByTagName('a');
for (var i=0 ; i