JavaScript学习--Item21 漂移的this

JavaScript学习--Item21 漂移的this,第1张

概述而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑。本文仅就这一问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What ’s this 问


而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就致使 JavaScript 中的 this 关键字有能力具有多重含义,带来灵活性的同时,也为初学者带来很多困惑。本文仅就这1问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What ’s this 问题,作为作者,我就会觉得花费这么多工夫,撰写这样1篇文章是值得的。

我们要记住1句话:this永久指向函数运行时所在的对象!而不是函数被创建时所在的对象。也即:谁调用,指向谁。切记…@H_502_15@

本文将分3种情况来分析this对象到底身处何方。

1、普通函数中的this

不管this身处何处,第1要务就是要找到函数运行时的位置。

var name="全局"; function getname(){ var name="局部"; return this.name; }; alert(getname());

当this出现在全局环境的函数getname中时,此时函数getname运行时的位置在

alert(getname());

明显,函数getname所在的对象是全局对象,即window,因此this的安身的地方定然在window。此时的this指向window对象,则getname返回的this.name实际上是window.name,因此alert出来的是“全局”!

那末,当this不是出现在全局环境的函数中,而是出现在局部环境的函数中时,又会身陷何方呢?

var name="全局"; var xpg={ name:"局部",getname:function(){ return this.name; } }; alert(xpg.getname());

其中this身处的函数getname不是在全局环境中,而是处在xpg环境中。不管this身处何处,1定要找到函数运行时的位置。此时函数getname运行时的位置

alert(xpg.getname());

明显,函数getname所在的对象是xpg,因此this的安身的地方定然在xpg,即指向xpg对象,则getname返回的this.name实际上是xpg.name,因此alert出来的是“局部”!

举个例子巩固1下:

var someone = { name: "Bob",showname: function(){ alert(this.name); }};var other = { name: "Tom",showname: someone.showname}other.showname();  //Tom

this关键字虽然是在someone.showname中声明的,但运行的时候是other.showname,所以this指向other.showname函数确当前对象,即other,故最后alert出来的是other.name。

2、闭包中的this

闭包也是个不安份子,本文暂且不对其过于赘述,简而言之:所谓闭包就是在1个函数内部创建另外一个函数,且内部函数访问了外部的变量。
浪子this与痞子闭包混在1起,可见将永毋宁日啊!

var name="全局"; var xpg={ name:"局部",getname:function(){ return function(){ return this.name; }; } }; alert(xpg.getname()());

此时的this明显身处窘境,居然处在getname函数中的匿名函数里面,而该匿名函数又调用了变量name,因此构成了闭包,即this身处闭包中。
不管this身处何处,1定要找到函数运行时的位置。此时不能根据函数getname运行时的位置来判断,而是根据匿名函数的运行时位置来判断。

function (){ return this.name;};

明显,匿名函数所在的对象是window,因此this的安身的地方定然在window,则匿名函数返回的this.name实际上是window.name,因此alert出来的就是“全局”!

那末,如何在闭包中使得this身处在xpg中呢?—缓存this@H_502_15@

var name="全局"; var xpg={ name:"局部",getname:function(){ var that=this; return function(){ return that.name; }; } }; alert(xpg.getname()());

在getname函数中定义that=this,此时getname函数运行时位置在

alert(xpg.getname());

则this指向xpg对象,因此that也指向xpg对象。在闭包的匿名函数中返回that.name,则此时返回的that.name实际上是xpg.name,因此就能够alert出来 “局部”!

3、new关键字创建新对象

new关键字后的构造函数中的this指向用该构造函数构造出来的新对象:

function Person(__name){ this.name = __name; //这个this指向用该构造函数构造的新对象,这个例子是Bob对象}Person.prototype.show = function(){ alert(this.name); //this 指向Person,this.name = Person.name;}var Bob = new Person("Bob");Bob.show(); //Bob4、call与apply中的this

在JavaScript中能管的住this的估计也就非call与apply莫属了。
call与apply就像this的父母1般,让this住哪它就得住哪,不能不听话!当无参数时,当前对象为window

var name="全局";var xpg={ name:"局部"};function getname(){ alert(this.name);}getname(xpg);getname.call(xpg);getname.call();

其中this身处函数getname中。不管this身处何处,1定要找到函数运行时的位置。此时函数getname运行时的位置

getname(xpg);

明显,函数getname所在的对象是window,因此this的安身的地方定然在window,即指向window对象,则getname返回的this.name实际上是window.name,因此alert出来的是“全局”!

那末,该call与apply登场了,由于this必须听他们的指挥!

getname.call(xpg);

其中,call指定this的安身的地方就是在xpg对象,由于this被迫只能在xpg那安家,则此时this指向xpg对象, this.name实际上是xpg.name,因此alert出来的是“局部”!

5、eval中的this

对eval函数,其履行时候仿佛没有指定当前对象,但实际上其this并不是指向window,由于该函数履行时的作用域是当前作用域,即同等于在该即将里面的代码填进去。下面的例子说明了这个问题:

var name = "window";var Bob = { name: "Bob",showname: function(){ eval("alert(this.name)"); }};Bob.showname(); //Bob6、没有明确确当前对象时的this

当没有明确的履行时确当前对象时,this指向全局对象window。
例如对全局变量援用的函数上我们有:

var name = "Tom";var Bob = { name: "Bob",show: function(){ alert(this.name); }}var show = Bob.show;show();  //Tom

你可能也能理解成show是window对象下的方法,所以履行时确当前对象时window。但局部变量援用的函数上,却没法这么解释:

var name = "window";var Bob = { name: "Bob",showname: function(){ alert(this.name); }};var Tom = { name: "Tom",showname: function(){ var fun = Bob.showname; fun(); }};Tom.showname();  //window

在阅读器中setTimeout、setInterval和匿名函数履行时确当前对象是全局对象window@H_502_15@,这条我们可以看成是上1条的1个特殊情况。

var name = "Bob"; var nameObj ={ name : "Tom",showname : function(){ alert(this.name); },waitShowname : function(){ setTimeout(this.showname,1000); } }; nameObj.waitShowname();

所以在运行this.showname的时候,this指向了window,所以最后显示了window.name。

7、dom事件中的this

(1)你可以直接在dom元素中使用

<input ID="btnTest" type="button" value="提交" onclick="alert(this.value))" />

分析:对dom元素的1个onclick(或其他如onblur等)属性,它为所属的HTML元素所具有,直接在它触发的函数里写this,this应当指向该HTML元素。

(2)给dom元素注册Js函数
a、不正确的方式

<script type="text/JavaScript"> function thisTest(){ alert(this.value); // d出undefined,this在这里指向??}</script><input ID="btnTest" type="button" value="提交" onclick="thistest()" />

分析:onclick事件直接调用thisTest函数,程序就会d出undefined。由于thisTest函数是在window对象中定义的,
所以thisTest的具有者(作用域)是window,thisTest的this也是window。而window是没有value属性的,所以就报错了。
b、正确的方式

<input ID="btnTest" type="button" value="提交" /><script type="text/JavaScript"> function thisTest(){ alert(this.value); }document.getElementByID("btnTest").onclick=thisTest; //给button的onclick事件注册1个函数</script>

分析:在前面的示例中,thisTest函数定义在全局作用域(这里就是window对象),所以this指代的是当前的window对象。而通过document.getElementByID(“btnTest”).onclick=thisTest;这样的情势,实际上是将btnTest的onclick属性设置为thisTest函数的1个副本,在btnTest的onclick属性的函数作用域内,this归btnTest所有,this也就指向了btnTest。其实如果有多个dom元素要注册该事件,我们可以利用不同的dom元素ID,用下面的方式实现:

document.getElementByID("domID").onclick=thisTest; //给button的onclick事件注册1个函数。

由于多个不同的HTML元素虽然创建了不同的函数副本,但每一个副本的具有者都是相对应的HTML元素,各自的this也都指向它们的具有者,不会造成混乱。
为了验证上陈述法,我们改进1下代码,让button直接d出它们对应的触发函数:

<input ID="btnTest1" type="button" value="提交1" onclick="thistest()" /><input ID="btnTest2" type="button" value="提交2" /><script type="text/JavaScript">function thisTest(){this.value="提交中";}var btn=document.getElementByID("btnTest1");alert(btn.onclick); //第1个按钮函数var btnOther=document.getElementByID("btnTest2");btnOther.onclick=thisTest;alert(btnOther.onclick); //第2个按钮函数</script>

其d出的结果是:

//第1个按钮function onclick(){ thistest()}//第2个按钮function thisTest(){ this.value="提交中";}

从上面的结果你1定理解的更透彻了。
By the way,每新建1个函数的副本,程序就会为这个函数副本分配1定的内存。而实际利用中,大多数函数其实不1定会被调用,因而这部份内存就被白白浪费了。所以我们通常都这么写:

<input ID="btnTest1" type="button" value="提交1" onclick="thisTest(this)" /><input ID="btnTest2" type="button" value="提交2" onclick="thisTest(this)" /><input ID="btnTest3" type="button" value="提交3" onclick="thisTest(this)" /><input ID="btnTest4" type="button" value="提交4" onclick="thisTest(this)" /><script type="text/JavaScript"> function thisTest(obj){ alert(obj.value); }</script>

这是由于我们使用了函数援用的方式,程序就只会给函数的本体分配内存,而援用只分配指针。这样写1个函数,调用的地方给它分配1个(指针)援用,这样效力就高很多。固然,如果你觉得这样注册事件不能兼容多种阅读器,可以写下面的注册事件的通用脚本:

//Js事件 添加 EventUtil.addEvent(dom元素,事件名称,事件触发的函数名) 移除EventUtil.removeEvent(dom元素,事件触发的函数名)var EventUtil = new eventManager();//Js事件通用管理器 dom元素 添加或移除事件function eventManager() { //添加事件 //odomElement:dom元素,如按钮,文本,document等; ****** oEventType:事件名称(如:click,如果是IE阅读器,自动将click转换为onclick);****** oFunc:事件触发的函数名 this.addEvent = function(odomElement,oEventType,oFunc) { //IE if (odomElement.attachEvent) { odomElement.attachEvent("on" + oEventType,oFunc); } //ff,opera,safari等 else if (odomElement.addEventListener) { odomElement.addEventListener(oEventType,oFunc,false); } //其他 else { odomElement["on" + oEventType] = oFunc; } } this.removeEvent = function(odomElement,oFunc) { //IE if (odomElement.detachEvent) { odomElement.detachEvent("on" + oEventType,safari等 else if (odomElement.removeEventListener) { odomElement.removeEventListener(oEventType,false); } //其他 else { odomElement["on" + oEventType] = null; } }}

正像注释写的那样,要注册dom元素事件,用EventUtil.addEvent(dom元素,事件触发的函数名)便可, 移除时可以这样写:EventUtil.removeEvent(dom元素,事件触发的函数名)。这是题外话,不说了。

总结

以上是内存溢出为你收集整理的JavaScript学习--Item21 漂移的this全部内容,希望文章能够帮你解决JavaScript学习--Item21 漂移的this所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1016267.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存