跟我学习javascript的this关键字


Posted in Javascript onMay 28, 2020

本文仅就这一问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What 's this 问题,作为作者,我就会觉得花费这么多功夫,撰写这样一篇文章是值得的。

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

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

1、普通函数中的this

无论this身处何处,第一要务就是要找到函数运行时的位置。

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身处何处,一定要找到函数运行时的位置。此时函数getName运行时的位置

alert(xpg.getName());

显然,函数getName所在的对象是xpg,因此this的安身之处定然在xpg,即指向xpg对象,则getName返回的this.name其实是xpg.name,因此alert出来的是“局部”!

举个例子巩固一下:

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

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

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

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

function (){
 return this.name;
};

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

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

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();  //Bob

4、call与apply中的this

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

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

其中this身处函数getName中。无论this身处何处,一定要找到函数运行时的位置。此时函数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(); //Bob

6、没有明确的当前对象时的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,这条我们可以看成是上一条的一个特殊情况。

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元素的一个onclick(或其他如onblur等)属性,它为所属的html元素所拥有,直接在它触发的函数里写this,this应该指向该html元素。

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

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

分析:onclick事件直接调用thisTest函数,程序就会弹出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事件注册一个函数
</script>

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

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

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

<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); //第一个按钮函数

var btnOther=document.getElementById("btnTest2");
btnOther.onclick=thisTest;
alert(btnOther.onclick); //第二个按钮函数
</script>
其弹出的结果是:

//第一个按钮
function onclick(){
 thisTest()
}

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

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

<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>

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

//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, oEventType, oFunc) {
  //ie
  if (oDomElement.detachEvent) {
   oDomElement.detachEvent("on" + oEventType, oFunc);
  }
  //ff,opera,safari等
  else if (oDomElement.removeEventListener) {
   oDomElement.removeEventListener(oEventType, oFunc, false);
  }
  //其他
  else {
   oDomElement["on" + oEventType] = null;
  }
 }
}

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

以上就是本文的全部内容,希望通过这篇文章大家更加了解javascript的this关键字,大家共同进步。

Javascript 相关文章推荐
jquery 批量上传图片实现代码
Jan 28 Javascript
Javascript 面向对象 继承
May 13 Javascript
JavaScript之编码规范 推荐
May 23 Javascript
原生JS实现响应式瀑布流布局
Apr 02 Javascript
Jquery左右滑动插件之实现超级炫酷动画效果附源码下载
Dec 02 Javascript
javascirpt实现2个iframe之间传值的方法
Jun 30 Javascript
JQuery组件基于Bootstrap的DropDownList(完整版)
Jul 05 Javascript
Bootstrap的fileinput插件实现多文件上传的方法
Sep 05 Javascript
js中小数向上取整数,向下取整数,四舍五入取整数的实现(必看篇)
Feb 13 Javascript
jQuery表单设置值的方法
Jun 30 jQuery
JS中定位 position 的使用实例代码
Aug 06 Javascript
JavaScript实现QQ列表展开收缩扩展功能
Oct 30 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
Nov 19 #Javascript
基于Jquery代码实现手风琴菜单
Nov 19 #Javascript
跟我学习javascript的作用域与作用域链
Nov 19 #Javascript
每天一篇javascript学习小结(属性定义方法)
Nov 19 #Javascript
理解 JavaScript Scoping &amp; Hoisting(二)
Nov 18 #Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
Nov 18 #Javascript
z-blog SyntaxHighlighter 长代码无法换行解决办法(基于jquery)
Nov 18 #Javascript
You might like
S900/ ETON E1-XM 收音机
2021/03/02 无线电
zen cart新进商品的随机排序修改方法
2010/09/10 PHP
thinkphp模板赋值与替换实例简述
2014/11/24 PHP
php实现图片局部打马赛克的方法
2015/02/11 PHP
py文件转exe时包含paramiko模块出错解决方法
2016/08/12 PHP
详解PHP中curl_multi并发的实现
2020/06/08 PHP
JQuery实现自定义对话框的代码
2008/06/15 Javascript
Jquery乱码的一次解决过程 图解教程
2010/02/20 Javascript
JS实现随机化快速排序的实例代码
2013/08/01 Javascript
IE下window.onresize 多次调用与死循环bug处理方法介绍
2013/11/12 Javascript
js 验证身份证信息有效性
2014/03/28 Javascript
js中for in语句的用法讲解
2015/04/24 Javascript
Jquery-1.9.1源码分析系列(十一)之DOM操作
2015/11/25 Javascript
jquery实现具有嵌套功能的选项卡
2016/02/12 Javascript
详解angularJs中自定义directive的数据交互
2017/01/13 Javascript
JavaScript字符集编码与解码详谈
2017/02/02 Javascript
AngularJS实现进度条功能示例
2017/07/05 Javascript
ztree简介_动力节点Java学院整理
2017/07/19 Javascript
JavaScript实现简单的双色球(实例讲解)
2017/07/31 Javascript
jQuery实现点击DIV同时点击CheckBox,并为DIV上背景色的实例
2017/12/18 jQuery
轻松搞定jQuery+JSONP跨域请求的解决方案
2018/03/06 jQuery
微信小程序使用map组件实现解析经纬度功能示例
2019/01/22 Javascript
vue cli使用融云实现聊天功能的实例代码
2019/04/19 Javascript
JS时间戳与日期格式互相转换的简单方法示例
2021/01/30 Javascript
[03:36]DOTA2完美大师赛coL战队趣味视频——我演你猜
2017/11/23 DOTA
python查找目录下指定扩展名的文件实例
2015/04/01 Python
Python中判断子串存在的性能比较及分析总结
2019/06/23 Python
python多线程共享变量的使用和效率方法
2019/07/16 Python
Python random库使用方法及异常处理方案
2020/03/02 Python
玩具反斗城西班牙网上商城:ToysRUs西班牙
2017/01/19 全球购物
软件售后服务承诺书
2014/05/21 职场文书
幼师求职信
2014/06/23 职场文书
以幸福为主题的活动方案
2014/08/22 职场文书
财务经理岗位职责范本
2015/04/08 职场文书
老乡会致辞
2015/07/28 职场文书
css清除浮动clearfix:after的用法详解(附完整代码)
2023/05/21 HTML / CSS