本文解析在JavaScript中将对象方法绑定到DOM事件时this指向错误的现象。通过示例说明直接绑定导致this指向DOM元素,进而介绍利用call或apply方法强制指定this上下文。文章详细讲解了createDele辅助函数的实现原理,通过返回匿名函数并调用call方法,确保事件触发时this正确指向原对象,从而获取正确的属性值。
DOM事件绑定中this指向错误的现象
首先观察以下现象:
参考以下代码:
<html>
<head>
<title>apply_and_call</title>
</head>
<body onload="init()">
<div id="testDiv" style="position: absolute; border: 1px solid gray; width:100px; height: 100px"></div>
<script type="text/javascript">
function init() {
var el = document.getElementById("testDiv");
var a = new classA(el);
}
function classA(el) {
this.a = 1;
this.container = el;
//绑定单击事件
this.container.onclick = this.click;
}
classA.prototype = {
click:function() {
alert(this.a);
}
}
</script>
</body>
</html> 当单击DIV元素后,弹出的对话框显示值为undefined。
造成该现象的原因是,当DOM对象响应单击事件时,事件处理函数内部的this关键字默认指向触发事件的DOM对象本身。由于该DOM对象并不具备属性a,因此访问this.a返回undefined。
然而,程序员的初衷是希望事件处理函数中的this能够指向classA的实例对象a。为了实现这一目标,需要借助call或apply方法来手动指定this的指向。
使用call方法修正this指向
接下来熟悉一下call方法的具体用法:
摘要:
function.call(thisobj, args…)
参数说明:
thisobj
作为调用function的对象。在函数主体内部,thisobj即为关键字this所引用的值。
args…
任意数量的参数,这些参数将被传递给函数function。
返回值:
调用函数function后的返回结果。
抛出异常:
TypeError
如果调用该方法的对象并非函数,则会抛出此异常。
描述:
call()方法将指定的函数function作为对象thisobj的方法来调用,并将thisobj之后的参数列表传递给它,返回值即为调用函数后的结果。在函数体内,关键字this引用的是thisobj对象。
若需将指定数组作为参数传递给函数,请使用Function.apply()方法。
在熟悉call()方法后,将代码1修改如下:
通过createDele函数实现上下文绑定
代码2示例:
参考以下代码:
<html>
<head>
<title>apply_and_call</title>
</head>
<body onload="init()">
<div id="testDiv" style="position: absolute; border: 1px solid gray; width:100px; height: 100px"></div>
<script type="text/javascript">
function init() {
var el = document.getElementById("testDiv");
var a = new classA(el);
}
function classA(el) {
this.t = 1;
this.clickDele = createDele(this.click, this);
el.onclick = this.clickDele;
}
classA.prototype = {
click:function() {
alert(this.t);
}
}
function createDele(fun, obj, arg) {
return function() {
return fun.call(obj, arg);
}
}
</script>
</body>
</html> 代码2第25行主要添加了createDele方法,该方法接收三个参数:fun(要执行的方法)、obj(fun中this需要指向的对象)以及arg(传入fun中的参数)。该方法最终返回一个匿名函数。
该匿名函数负责执行fun方法,同时将fun内部的this指向obj,并使用arg作为参数传入,最后返回处理结果。
当程序运行至第15行调用createDele方法时,传入对象的方法及对象本身。createDele接收参数后返回一个匿名方法,并将this.clickDele设置为该返回的匿名方法。第16行代码将this.clickDele(即匿名方法)绑定到DOM事件上。程序执行完毕后,点击DOM(DIV)触发匿名方法。此时,匿名方法中的fun为之前传入的this.click(即方法a.click),obj为之前传入的this(即对象a)。因此,通过call方法使得this.click(即方法a.click)中的this指向obj(即对象a),最终弹出结果为1。结果正确,达到了程序设计的初衷。
回顾匿名函数的使用可能会让人感到有些困惑:为何调用匿名方法时fun会是this.click(即方法a.click),而obj会是this(即对象a)?要解答这个问题,需要引入JavaScript的闭包概念。此处暂不深入介绍闭包,后续将发表关于JavaScript闭包的文章,欢迎感兴趣的朋友持续关注!
无论读者是否完全理解,上述原理和程序逻辑均无误。:)