zoukankan      html  css  js  c++  java
  • Javascript中this关键字详解

    Javascript的this用法

     

    作者: 阮一峰

    日期: 2010年4月30日

    this是Javascript语言的一个关键字。

    它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。比如,

      function test(){

        this.x = 1;

      }

    随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。

    下面分四种情况,详细讨论this的用法。

    情况一:纯粹的函数调用

    这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。

    请看下面这段代码,它的运行结果是1。

      function test(){

        this.x = 1;

        alert(this.x);

      }

      test(); // 1

    为了证明this就是全局对象,我对代码做一些改变:

      var x = 1;

      function test(){

        alert(this.x);

      }

      test(); // 1

    运行结果还是1。再变一下:

      var x = 1;

      function test(){

        this.x = 0;

      }

      test();

      alert(x); //0

    情况二:作为对象方法的调用

    函数还可以作为某个对象的方法调用,这时this就指这个上级对象。

      function test(){

        alert(this.x);

      }

      var o = {};

      o.x = 1;

      o.m = test;

      o.m(); // 1

    情况三 作为构造函数调用

    所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。

      function test(){

        this.x = 1;

      }

      var o = new test();

      alert(o.x); // 1

    运行结果为1。为了表明这时this不是全局对象,我对代码做一些改变:

      var x = 2;

      function test(){

        this.x = 1;

      }

      var o = new test();

      alert(x); //2

    运行结果为2,表明全局变量x的值根本没变。

    情况四 apply调用

    apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。

      var x = 0;

      function test(){

        alert(this.x);

      }

      var o={};

      o.x = 1;

      o.m = test;

      o.m.apply(); //0

    apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。

    如果把最后一行代码修改为

      o.m.apply(o); //1

    运行结果就变成了1,证明了这时this代表的是对象o。

    闭包里面的THIS:

    请看下面的代码,最后alert出来的是什么呢?

     1 var name = "Bob";  
     2 var nameObj ={  
     3     name : "Tom",  
     4     showName : function(){  
     5         alert(this.name);  
     6     },  
     7     waitShowName : function(){  
     8         setTimeout(this.showName, 1000);  
     9     }  
    10 };  
    11 
    12 nameObj.waitShowName();

    this指向哪里?

    一般而言,在Javascript中,this指向函数执行时的当前对象。

    In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked.

    ——jQuery Fundamentals (Chapter 2), by Rebecca Murphey

    值得注意,该关键字在Javascript中和执行环境,而非声明环境有关。

    The this keyword is relative to the execution context, not the declaration context.

    我们举个例子来说明这个问题:

    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。

    没有明确的当前对象时

    当没有明确的执行时的当前对象时,this指向全局对象window。

    By default, this refers to the global object.

    为什么说是全局对象(the global object),因为非浏览器情况下(例如:nodejs)中全局变量并非window对象,而就是叫“全局变量”(the global object)。不过由于我们这片文章主要讨论的是前端开发知识,所以nodejs就被我们忽略了。

    例如对于全局变量引用的函数上我们有:

    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和匿名函数

    文章开头的问题的答案是Bob。

    在浏览器中setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window,这条我们可以看成是上一条的一个特殊情况。

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

    浏览器中全局变量可以当成是window对象下的变量,例如全局变量a,可以用window.a来引用。

    我们将代码改成匿名函数可能更好理解一些:

    var name = "Bob";  
     var nameObj ={  
         name : "Tom",  
         showName : function(){  
             alert(this.name);  
         },  
         waitShowName : function(){  
             function(__callback){
                __callback();
            }(this.showName);  
         }  
     };  
     
     nameObj.waitShowName();  //Bob

    在调用nameObj.waitShowName时候,我们运行了一个匿名函数,将nameObj.showName作为回调函数传进这个匿名函数,然后匿名函数运行时,运行这个回调函数。由于匿名函数的当前对象是window,所以当在该匿名函数中运行回调函数时,回调函数的this指向了window,所以alert出来window.name。

    由此看来setTimeout可以看做是一个延迟执行的:

    function(__callback){
        __callback();
    }

    setInterval也如此类比。

    但如果我们的确想得到的回答是Tom呢?通过一些技巧,我们能够得到想要的答案:

    var name = "Bob";  
    var nameObj ={  
        name : "Tom",  
        showName : function(){  
            alert(this.name);  
        },  
        waitShowName : function(){
            var that = this;
            setTimeout(function(){
                that.showName();
            }, 1000);
        }
    }; 
     
     nameObj.waitShowName();  //Tom

    在执行nameObj.waitShowName函数时,我们先对其this赋给变量that(这是为了避免setTimeout中的匿名函数运行时,匿名函数中的this指向window),然后延迟运行匿名函数,执行that.showName,即nameObj.showName,所以alert出正确结果Tom。

    eval

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

    var name = "window";
    
    var Bob = {
        name: "Bob",
        showName: function(){
            eval("alert(this.name)");
        }
    };
    
    Bob.showName();    //Bob

    apply和call

    apply和call能够强制改变函数执行时的当前对象,让this指向其他对象。因为apply和call较为类似,所以我们以apply为例:

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

    apply用于改变函数执行时的当前对象,当无参数时,当前对象为window,有参数时当前对象为该参数。于是这个例子Bob成功偷走了Tom的名字。

    new关键字

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

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

    思考题

    1.  请问下面代码会alert出什么,为什么?

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

    2.  请问下面代码会alert出什么,为什么?

    var fun = new Function("alert(this)");
    fun();

    3.  下面代码分别在IE和其他浏览器上运行有什么差异,可以用什么方法解决这个差异问题?

    IE:

    <button id = "box" name = "box">Click Me!</button>
    
    <script>
        var name = "window";
    
        function showName(){
            alert(this.name);
        }
    
        document.getElementById("box").attachEvent("onclick", showName);
    </script>

    Others:

    <button id = "box" name = "box">Click Me!</button>
    
    <script>
        var name = "window";
    
        function showName(){
            alert(this.name);
        }
    
        document.getElementById("box").addEventListener("click", showName, false);
    </script>
  • 相关阅读:
    浏览器内核中各个线程之间的关系
    Browser进程和浏览器内核(Renderer进程)的通信过程
    babel 的一些记录
    mac nvm install
    小程序云开发补充
    JavaScript 导学推荐
    网页切片
    初探响应式Web设计
    WEB ICON 的探讨
    [转载]CSS 创作指南(Beta)(css规范)
  • 原文地址:https://www.cnblogs.com/littlewrong/p/4924534.html
Copyright © 2011-2022 走看看