zoukankan      html  css  js  c++  java
  • javascript的this关键字

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

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

    本文将分三种情况来分析this对象到底身处何方。
    1、普通函数中的this
    无论this身处何处,第一要务就是要找到函数运行时的位置。

    1
    2
    3
    4
    5
    6
    var name="全局";
    function getName(){
      var name="局部";
      return this.name;
    };
    alert(getName());

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

    1
    alert(getName());

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    var name="全局";
    var xpg={
      name:"局部",
      getName:function(){
        return this.name;
      }
    };
    alert(xpg.getName());

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

    1
    alert(xpg.getName());

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

    举个例子巩固一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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与痞子闭包混在一起,可见将永无宁日啊!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var name="全局";
    var xpg={
      name:"局部",
      getName:function(){
        return function(){
          return this.name;
        };
      }
    };
    alert(xpg.getName()());

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

    1
    2
    3
    function (){
      return this.name;
    };

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var name="全局";
    var xpg={
      name:"局部",
      getName:function(){
        var that=this;
        return function(){
          return that.name;
        };
      }
    };
    alert(xpg.getName()());

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

    1
    alert(xpg.getName());

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

    3、new关键字创建新对象

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var name="全局";
    var xpg={
      name:"局部"
    };
    function getName(){
      alert(this.name);
    }
    getName(xpg);
    getName.call(xpg);
    getName.call();

    其中this身处函数getName中。无论this身处何处,一定要找到函数运行时的位置。此时函数getName运行时的位置

    1
    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,因为该函数执行时的作用域是当前作用域,即等同于在该行将里面的代码填进去。下面的例子说明了这个问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var name = "window";
     
    var Bob = {
      name: "Bob",
      showName: function(){
        eval("alert(this.name)");
      }
    };
     
    Bob.showName();  //Bob

    6、没有明确的当前对象时的this

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var name = "Tom";
     
    var Bob = {
      name: "Bob",
      show: function(){
        alert(this.name);
      }
    }
     
    var show = Bob.show;
    show();  //Tom

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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,这条我们可以看成是上一条的一个特殊情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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元素中使用

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

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

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

    1
    2
    3
    4
    5
    6
    <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、正确的方式

    1
    2
    3
    4
    5
    6
    7
    8
    <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,用下面的方式实现:

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <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,每新建一个函数的副本,程序就会为这个函数副本分配一定的内存。而实际应用中,大多数函数并不一定会被调用,于是这部分内存就被白白浪费了。所以我们通常都这么写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    //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元素,事件名称,事件触发的函数名),这是题外话,不说了。

  • 相关阅读:
    Java程序员必知的8大排序
    java提高篇-----理解java的三大特性之封装
    树莓派学习笔记——GPIO功能学习
    SQL 服务没有及时响应启动或控制请求”的解决方法
    http://blog.csdn.net/u011001723/article/details/45621027
    error
    Spring @Conditional注解的使用
    Python [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed 解决方法
    python
    local class incompatible: stream classdesc serialVersionUID = -2897844985684768944, local class serialVersionUID = 7350468743759137184
  • 原文地址:https://www.cnblogs.com/wangkang1/p/5899269.html
Copyright © 2011-2022 走看看