Javascript中this关键字详解

Quiz

请看下面的代码,最后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();  //Bob
复制代码

要解决这个问题我们需要了解Javascript的this关键字的用法。

 

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>
复制代码

 

参考文献

Javascript Closures . Richard Cornford  . March 2004

Javascript的this用法 . 阮一峰 . 2010.4.30

好文要顶  关注我  收藏该文   
Justany_WhiteSnow
关注 - 0
粉丝 - 269
+加关注
17
0
(请您对文章做出评价)
» 下一篇: 关联数据入门——RDF
ADD YOUR COMMENT

  1. #1楼 林J  2012-11-01 08:59
    支持一记,很有用。
    支持(0) 反对(0)
  2. #2楼 bluescreen  2012-11-01 09:01
    1.第一个思考题你代码是不是错了? setTimeout("that.showName();", 1000); 这句会报错吧!如有不对指出还望谅解指证!谢谢!
    是不是该这样呢!
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var  name =  "Bob"
    var  nameObj ={ 
         name :  "Tom"
         showName :  function (){ 
             alert( this .name); 
         }, 
         waitShowName :  function (){
             var  that =  this ;
             setTimeout(that.showName(), 1000);
         }
    };
      
    nameObj.waitShowName();
    支持(0) 反对(0)
  3. #3楼[楼主] Justany_WhiteSnow  2012-11-01 09:16
    @bluescreen
    引用1.第一个思考题你代码是不是错了? setTimeout("that.showName();", 1000); 这句会报错吧!如有不对指出还望谅解指证!谢谢!
    是不是该这样呢!

    答案就是会报错,有兴趣的话考虑下为什么吧。

    哦,刚没看你的代码。你的代码是没什么意义的。因为that.showName在传进setTimeout之前已经执行了。
    支持(1) 反对(0)
  4. #4楼 bluescreen  2012-11-01 09:29
    @Justany_WhiteSnow
    引用@bluescreen
    引用引用1.第一个思考题你代码是不是错了? setTimeout("that.showName();", 1000); 这句会报错吧!如有不对指出还望谅解指证!谢谢!
    是不是该这样呢!

    答案就是会报错,有兴趣的话考虑下为什么吧。

    哦,刚没看你的代码。你的代码是没什么意义的。因为that.showName在传进setTimeout之前已经执行了。

    对!是that.showName在传进setTimeout之前已经执行了。无论时间如何设置都会!
    支持(0) 反对(0)
  5. #5楼 林J  2012-11-01 09:32
    @Justany_WhiteSnow
    会报错的原因是因为setTimeout对象是windows,相当于执行的是window.that.showName(),这里that没有定义?
    这样的话这个题目没什么意义啊。我是想看如何用setTimeout弹出Tom的。。
    支持(0) 反对(0)
  6. #6楼[楼主] Justany_WhiteSnow  2012-11-01 09:37
    @林J
    引用@Justany_WhiteSnow
    会报错的原因是因为setTimeout对象是windows,相当于执行的是window.that.showName(),这里that没有定义?
    这样的话这个题目没什么意义啊。我是想看如何用setTimeout弹出Tom的。。
    弹出Tom的方法,原文有啊。
    也不能说没有意义。思考题1是为了对比:
    setTimeout(函数名, 延迟)
    setTimeout(匿名函数, 延迟)
    setTimeout(字符串代码, 延迟)
    这是三种方法的差异的。
    支持(0) 反对(0)
  7. #7楼 林J  2012-11-01 09:45
    @Justany_WhiteSnow
    哦对。顺便请教楼主一个问题啊:setTimeout(that.showName(),1000),setTimeout(that.showName,1000),
    setTimeout("that.showName()",1000)这三种写法在解析的时候有什么区别呢?我只知道加‘()’的会直接执行,但是不知道解析器究竟干了什么。
    支持(0) 反对(0)
  8. #8楼[楼主] Justany_WhiteSnow  2012-11-01 10:02
    @林J
    引用@Justany_WhiteSnow
    哦对。顺便请教楼主一个问题啊:setTimeout(that.showName(),1000),setTimeout(that.showName,1000),
    setTimeout("that.showName()",1000)这三种写法在解析的时候有什么区别呢?我只知道加‘()’的会直接执行,但是不知道解析器究竟干了什么。
    1.setTimeout(that.showName(),1000)
    that.showName是函数引用,that.showName()是函数运行。这种传递方式真正传进去的是that.showName函数的返回值。
    2.setTimeout(that.showName,1000)
    个人理解是相当于一个延迟执行的
    1
    2
    3
    (function(__callback){
         __callback();
    })(that.showName);

    这个我已在正文说明了。
    3.setTimeout("that.showName()",1000)
    相当于一个延迟执行的
    1
    ( new  Function( "that.showName()" ))()


    具体来说setTimeout(字符串代码, 延迟)是在一定延迟之后用字符串代码创建一个新的函数,因为该函数没有明确的当前对象,所以this指向全局变量。当然这里还有一个问题为什么that没法正常引用,因为他重新建立一个执行环境(context),且这个执行环境和原来执行环境无关。

    我想思考题1和2的答案也很清晰了。
    支持(0) 反对(0)
  9. #9楼 技术屌丝  2012-11-01 10:27
    已懵圈 @_@
    支持(0) 反对(0)
  10. #10楼 林J  2012-11-01 10:29
    @Justany_WhiteSnow
    引用@林J
    引用引用@Justany_WhiteSnow
    哦对。顺便请教楼主一个问题啊:setTimeout(that.showName(),1000),setTimeout(that.showName,1000),
    setTimeout("that.showName()",1000)这三种写法在解析的时候有什么区别呢?我只知道加‘()’的会直接执行,但是不知道解析器究竟干了什么。
    1.setTimeout(that.showName(),1000)
    that.showName是函数引用,that.showName()是函数运行。这种传递方式真正传进去的是that.sho...
    感谢回答,第三种用字符串代码创建一个新的函数,和eval方法有点像啊。我以前看过有文章说过最好不用使用eval方法的,因为它会执行任意传给它的代码,不管对错。一般情况下还是不要使用setTimeout(字符串代码, 延迟)好点吧。
    支持(0) 反对(0)
  11. #11楼 Coolicer  2012-11-01 10:36
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var  name =  "Bob"
    var  nameObj ={ 
         name :  "Tom"
         showName :  function (){ 
             alert( this .name); 
         }, 
         waitShowName :  function (){
             var  that =  this ;
             setTimeout( "that.showName();" , 1000);  //setTimeout("nameObj.showName();", 1000);
    // 这里的写法应该相当于eval效果? 而setTimeout指向的this是window,
    //所以window中没有showName这个方法,把that改成nameObj就可以弹出Tom
         }
    };
    nameObj.waitShowName();
    支持(0) 反对(1)
  12. #12楼[楼主] Justany_WhiteSnow  2012-11-01 10:44
    @Coolicer
    参见8楼。eval和new Function还是有一定差别的
    支持(0) 反对(0)
  13. #13楼 Coolicer  2012-11-01 10:58
    @Justany_WhiteSnow
    New Function 文章好像没有吧,New Function ,with, eval,setTimeout都是比较奇怪的东东,看着还真会晕。
    支持(0) 反对(0)
  14. #14楼 Iyanzi  2012-11-01 15:45
    受教了,,顶
    支持(0) 反对(0)
  15. #15楼 松下裤腰带  2012-11-01 20:22
    学习了。
    支持(0) 反对(0)
  16. #16楼 PEPE YU  2012-11-02 16:48
    var show = Bob.show;
    show();  //Tom

    ==>
    Bob.show()//Bob
    支持(0) 反对(0)
  17. #17楼 PEPE YU  2012-11-02 17:00
    总结的好
    支持(0) 反对(0)
  18. #18楼 NinoFocus  2012-11-08 17:22
    第三题:
    attachEvent的回调函数中的this关键字指向了window对象,而不是当前元素(IE的一个巨大缺点)。
    addEventListener的回调函数中的this关键字指向了当前被点击的元素。

    具体的可以看这篇文章: http://www.cnblogs.com/ninofocus/archive/2012/11/07/javascript-event-bind.html
    支持(0) 反对(0)
  19. #19楼 翱翔软件  2012-11-15 11:21
    嗯,不错啊
    还有,
    在开发时,可以采用两种方式,一种是命名空间的方式,一种是面向对象的方式。
    支持(0) 反对(0)
  20. #20楼 Albert_1  2012-12-25 10:15
    var name = "Bob"; 
    var nameObj ={ 
    name : "Tom", 
    showName : function(){ 
    alert(this.name); 
    }, 
    waitShowName : function(){
    // var nameObj = this;
    setTimeout("nameObj.showName();", 1000);
    }
    }; 

    nameObj.waitShowName();
    支持(0) 反对(0)
  21. #21楼 Mr Code  2013-01-31 10:54
    @林J
    引用@Justany_WhiteSnow
    会报错的原因是因为setTimeout对象是windows,相当于执行的是window.that.showName(),这里that没有定义?
    这样的话这个题目没什么意义啊。我是想看如何用setTimeout弹出Tom的。。
    弹出tom:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var  name =  "Bob"
    var  nameObj ={ 
         name :  "Tom"
         showName :  function (){ 
             alert( this .name); 
         }, 
         waitShowName :  function (){
             var  that =  this ;
             setTimeout( function (){that.showName();}, 1000);  //setTimeout("nameObj.showName();", 1000);
    // 这里的写法应该相当于eval效果? 而setTimeout指向的this是window,
    //所以window中没有showName这个方法,把that改成nameObj就可以弹出Tom
         }
    };
    nameObj.waitShowName();
    支持(0) 反对(0)
  22. #22楼 早起的菜鸟  2013-07-12 15:59
    涨姿势了
    支持(0) 反对(0)
  23. #23楼 snov  2015-01-16 00:50
    "没有明确的当前对象时" 这个说法本身就有问题,太含糊了,什么叫没有明确对象,每个函数运行时所属的对象都是明确的,只是有些人不明确。

    第二个例子我觉得比较典型。但是结果为什么是这样呢。我想谈下自己的看法。
    例子1
    var name = "window";

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

    var Tom = {
    name: "Tom",
    showName: function(){
    Bob.showName();
    }
    };

    Tom.showName();  //Bob
    因为showName()函数的对象是Bob,所以this是指的Bob;

    例子二
    var name = "window";

    var Bob = {
    name: "Bob",
    showName: function(){
    alert(this.name);
    }
    };
    var fun = Bob.showName;
    fun();   //window
    Bob.showName的函数赋值给fun,由于Bob是由匿名类构造的,赋值的也只是函数,及回调函数,本身是没有对象的。所以showName中的this就没有对象了,没有对象时就是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
    给fun赋值为Bob.showName时,因为fun本身是临时变量(或者叫私有变量),也就没有所属对象;因为Bob.showName赋值的是函数,相当于回调函数,是没有对象的,fun和Bob.showName回调都没有对象,所以没有对象时还是window。

    在看下面这种情况,
    var name = "window";

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

    var Tom = {
    name: "Tom",
    fun: undefined,
    showName: function(){
    this.fun = Bob.showName;
    this.fun();

    }
    };

    Tom.showName();  //Tom
    虽然赋值的Bob.showName是回调函数,没有对象,但fun是有所属对象的就是Tom,所以结果是Tom。如果按你的说法"没有明确的当前对象时",this首先是Bob里,然后又是Tom里,不太明确,推理结果是window,这显然是和实际运行不符的。
    本人c++程序员,初学javascript
    支持(0) 反对(0)
  24. #24楼 孙可瘦  2015-05-30 15:39




    Document








    这样才会弹出tom对吧~




深入浅出 JavaScript 中的 this

JavaScript 是一种脚本语言,因此被很多人认为是简单易学的。然而情况恰恰相反,JavaScript 支持函数式编程、闭包、基于原型的继承等高级功能。本文仅采撷其中的一例:JavaScript 中的 this 关键字,深入浅出的分析其在不同情况下的含义,形成这种情况的原因以及 Dojo 等 JavaScript 工具中提供的绑定 this 的方法。可以这样说,正确掌握了 JavaScript 中的 this 关键字,才算迈入了 JavaScript 这门语言的门槛。

王 群锋, 软件工程师, IBM

2012 年 7 月 09 日

  • expand内容

在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象。一般在编译期确定下来,或称为编译期绑定。而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑。本文仅就这一问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What ’s this 问题,作为作者,我就会觉得花费这么多功夫,撰写这样一篇文章是值得的。

Java 语言中的 this

在 Java 中定义类经常会使用 this 关键字,多数情况下是为了避免命名冲突,比如在下面例子的中,定义一个 Point 类,很自然的,大家会使用 x,y 为其属性或成员变量命名,在构造函数中,使用 x,y 为参数命名,相比其他的名字,比如 a,b,也更有意义。这时候就需要使用 this 来避免命名上的冲突。另一种情况是为了方便的调用其他构造函数,比如定义在 x 轴上的点,其 x 值默认为 0,使用时只要提供 y 值就可以了,我们可以为此定义一个只需传入一个参数的构造函数。无论哪种情况,this 的含义是一样的,均指当前对象。

清单 1. Point.java
 public class Point { 
    private int x = 0; 
    private int y = 0; 
    
    public Point(x, y){ 
        this.x = x; 
        this.y = y; 
    } 
    
    public Point(y){ 
        this(0, y); 
    } 
 }

JavaScript 语言中的 this

由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。下面我们将按照调用方式的不同,分别讨论 this 的含义。

作为对象方法调用

在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象。

清单 2. point.js
 var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
     this.x = this.x + x; 
     this.y = this.y + y; 
     } 
 }; 

 point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象

作为函数调用

函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。比如下面的例子:函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的。

清单 3. nonsense.js
 function makeNoSense(x) { 
 this.x = x; 
 } 

 makeNoSense(5); 
 x;// x 已经成为一个值为 5 的全局变量

对于内部函数,即声明在另外一个函数体内的函数,这种绑定到全局对象的方式会产生另外一个问题。我们仍然以前面提到的 point 对象为例,这次我们希望在 moveTo 方法内定义两个函数,分别将 x,y 坐标进行平移。结果可能出乎大家意料,不仅 point 对象没有移动,反而多出两个全局变量 x,y。

清单 4. point.js
 var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
     // 内部函数
     var moveX = function(x) { 
     this.x = x;//this 绑定到了哪里?
    }; 
    // 内部函数
    var moveY = function(y) { 
    this.y = y;//this 绑定到了哪里?
    }; 

    moveX(x); 
    moveY(y); 
    } 
 }; 
 point.moveTo(1, 1); 
 point.x; //==>0 
 point.y; //==>0 
 x; //==>1 
 y; //==>1

这属于 JavaScript 的设计缺陷,正确的设计方式是内部函数的 this 应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,聪明的 JavaScript 程序员想出了变量替代的方法,约定俗成,该变量一般被命名为 that。

清单 5. point2.js
 var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
      var that = this; 
     // 内部函数
     var moveX = function(x) { 
     that.x = x; 
     }; 
     // 内部函数
     var moveY = function(y) { 
     that.y = y; 
     } 
     moveX(x); 
     moveY(y); 
     } 
 }; 
 point.moveTo(1, 1); 
 point.x; //==>1 
 point.y; //==>1

作为构造函数调用

JavaScript 支持面向对象式编程,与主流的面向对象式编程语言不同,JavaScript 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式。相应的,JavaScript 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样。作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用。如果调用正确,this 绑定到新创建的对象上。

清单 6. Point.js
 function Point(x, y){ 
    this.x = x; 
    this.y = y; 
 }

使用 apply 或 call 调用

让我们再一次重申,在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。很多 JavaScript 中的技巧以及类库都用到了该方法。让我们看一个具体的例子:

清单 7. Point2.js
 function Point(x, y){ 
    this.x = x; 
    this.y = y; 
    this.moveTo = function(x, y){ 
        this.x = x; 
        this.y = y; 
    } 
 } 

 var p1 = new Point(0, 0); 
 var p2 = {x: 0, y: 0}; 
 p1.moveTo(1, 1); 
 p1.moveTo.apply(p2, [10, 10]);

在上面的例子中,我们使用构造函数生成了一个对象 p1,该对象同时具有 moveTo 方法;使用对象字面量创建了另一个对象 p2,我们看到使用 apply 可以将 p1 的方法应用到 p2 上,这时候 this 也被绑定到对象 p2 上。另一个方法 call 也具备同样功能,不同的是最后的参数不是作为一个数组统一传入,而是分开传入的。

换个角度理解

如果像作者一样,大家也觉得上述四种方式不方便记忆,过一段时间后,又搞不明白 this 究竟指什么。那么我向大家推荐 Yehuda Katz 的这篇文章:Understanding JavaScript Function Invocation and “this”。在这篇文章里,Yehuda Katz 将 apply 或 call 方式作为函数调用的基本方式,其他几种方式都是在这一基础上的演变,或称之为语法糖。Yehuda Katz 强调了函数调用时 this 绑定的过程,不管函数以何种方式调用,均需完成这一绑定过程,不同的是,作为函数调用时,this 绑定到全局对象;作为方法调用时,this 绑定到该方法所属的对象。

结束?

通过上面的描述,如果大家已经能明确区分各种情况下 this 的含义,这篇文章的目标就已经完成了。如果大家的好奇心再强一点,想知道为什么 this 在 JavaScript 中的含义如此丰富,那就得继续阅读下面的内容了。作者需要提前告知大家,下面的内容会比前面稍显枯燥,如果只想明白 this 的含义,阅读到此已经足够了。如果大家不嫌枯燥,非要探寻其中究竟,那就一起迈入下一节吧。

函数的执行环境

JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因。一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments变量,其中包含调用函数时传入的参数。接下来创建作用域链。然后初始化变量,首先初始化函数的形参表,值为 arguments变量中对应的值,如果 arguments变量中没有对应值,则该形参初始化为 undefined。如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,鉴于篇幅,我们先不在这里讨论这个话题。最后为 this变量赋值,如前所述,会根据函数调用方式的不同,赋给 this全局对象,当前对象等。至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。

Function.bind

有了前面对于函数执行环境的描述,我们来看看 this 在 JavaScript 中经常被误用的一种情况:回调函数。JavaScript 支持函数式编程,函数属于一级对象,可以作为参数被传递。请看下面的例子 myObject.handler 作为回调函数,会在 onclick 事件被触发时调用,但此时,该函数已经在另外一个执行环境(ExecutionContext)中执行了,this 自然也不会绑定到 myObject 对象上。

清单 8. callback.js
 button.onclick = myObject.handler;

这是 JavaScript 新手们经常犯的一个错误,为了避免这种错误,许多 JavaScript 框架都提供了手动绑定 this 的方法。比如 Dojo 就提供了 lang.hitch,该方法接受一个对象和函数作为参数,返回一个新函数,执行时 this 绑定到传入的对象上。使用 Dojo,可以将上面的例子改为:

清单 9. Callback2.js
 button.onclick = lang.hitch(myObject, myObject.handler);

在新版的 JavaScript 中,已经提供了内置的 bind 方法供大家使用。

eval 方法

JavaScript 中的 eval 方法可以将字符串转换为 JavaScript 代码,使用 eval 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval 方法,调用者的执行环境(ExecutionContext)中的 this 就被 eval 方法继承下来了。

结束语

本文介绍了 JavaScript 中的 this 关键字在各种情况下的含义,虽然这只是 JavaScript 中一个很小的概念,但借此我们可以深入了解 JavaScript 中函数的执行环境,而这是理解闭包等其他概念的基础。掌握了这些概念,才能充分发挥 JavaScript 的特点,才会发现 JavaScript 语言特性的强大。






javascript this用法小结

作者: 字体:[ 增加 减小] 类型:转载 时间:2008-12-19
this是JavaScript中功能最强大的关键字之一。不幸的是,如果你不知道它具体怎么工作,你将很难正确使用它。
this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象。但是在javascript中,由于 javascript的动态性(解释执行,当然也有简单的预编译过程),this的指向在运行时才确定。这个特性在给我们带来迷惑的同时也带来了编程上的 自由和灵活,结合apply(call)方法,可以使JS变得异常强大。
2.变化的this
在JavaScript中,this通常指向的是我们正在执行的函数本身,或者是指向该函数所属的对象(运行时)。当我们在页面中定义了函数 doSomething()的时候,它的owner是页面,或者是JavaScript中的window对象(或 global对象)。对于一个onclick属性,它为它所属的HTML元素所拥有,this应该指向该HTML元素。
2.1在几种常见场景中this的变化
函数示例
function doSomething ()
{
alert(this.navigator); //appCodeName
this.value = "I am from the Object constructor";
this.style.backgroundColor = "# 000000";
}
1. (A)作为普通函数直接调用时,this指向window对象.
2. (B)作为控件事件触发时
1) inline event registration 内联事件注册 .将事件直接写在HTML代码中(οnclick=”doSomething()”>), 此时this指向 window对象 。
2) Traditional event registration 传统事件注册 (DHTML方式).
形如 element.onclick = doSomething; 此时this指向 element对象
3) 作为参数传递可以指向element
3. (C)作为对象使用时this指向当前对象。形如:new doSomething();
4. (D)使用apply 或者call方法时,this指向所传递的对象。
形如:var obj={}; doSomething.apply(obj,new Array(”nothing”); //thisàobj

下面我来阐述如何在事件处理中来使用this,之后我会附加一些this相关的例子。
Owner
接下来文章中我们将要讨论的问题是:在函数doSomething()中this所指的是什么?
Javascript代码
function doSomething() {
this.style.color = '#cc0000';
}
function doSomething() {
this.style.color = '#cc0000';
}
在JavaScript中,this通常指向的是我们正在执行的函数本身(译者注:用owner代表this所指向的内容),或者是,指向该函数所属的对象。当我们在页面中定义了函数doSomething()的时候,它的owner是页面,或者是JavaScript中的window对象(或 global对象)。对于一个onclick属性,它为它所属的HTML元素所拥有,this应该指向该HTML元素。
这种“所有权”就是JavaScript中面向对象的一种方式。在Objects as associative arrays中可以查看一些更多的信息。
Javascript中this关键字详解_第1张图片 
如果我们在没有任何更多准备情况下执行doSomething(),this关键字会指向window,并且该函数试图改变window的 style.color。因为window并没有style对象,这个函数将非常不幸的运行失败,并产生JavaScript错误。
Copying
因此如果我们想充分利用this,我们不得不注意使用this的函数应该被正确的HTML元素所拥有。换句话说,我们应该复制这个函数到我们的onclick属性。Traditional event registration会关心它。
Javascript代码
element.onclick = doSomething;
element.onclick = doSomething;
这个函数被完整复制到onclick属性(现在成为了函数)。因此如果这个event handler被执行,this将指向HTML元素,并且该元素的颜色得以改变。
Javascript中this关键字详解_第2张图片 
这种方法使得我们能够复制这个函数到多个event handler。每次this都将指向正确的HTML元素:
Javascript中this关键字详解_第3张图片 
这样你就可以最大限度使用this。每当执行该函数,this所指向的HTML元素都正确响应事件,这些HTML元素拥有doSomething()的一个拷贝。
Referring
然而,如果你使用inline event registration(内联事件注册)
Javascript代码


你将不能拷贝该函数!反而这种差异是非常关键的。onclick属性并不包含实际的函数,仅仅是一个函数调用。
Javascript代码
doSomething();
doSomething();
因此,它将声明“转到doSomething()并且执行它”。当我们到达doSomething(),this关键字又重新指向了全局的window对象,函数返回错误信息。
Javascript中this关键字详解_第4张图片 
The difference
如果你想使用this来指向HTML元素响应的事件,你必须确保this关键字被写在onclick属性里。只有在这种情况下它才指向event handler所注册的HTML元素。
Javascript代码
element.onclick = doSomething;
alert(element.onclick)
element.onclick = doSomething;
alert(element.onclick)
你将得到
Javascript代码
function doSomething() {
this.style.color = '#cc0000';
}
function doSomething() {
this.style.color = '#cc0000';
}
正如你所见,this关键字被展现在onclick函数中,因此它指向HTML元素。
但是如果执行
Javascript代码

alert(element.onclick)

alert(element.onclick)
你将得到
Javascript代码
function onclick() {
doSomething()
}
function onclick() {
doSomething()
}
这仅仅是到doSomething()函数的一个引用。this关键字并没有出现在onclick函数中,因此它不指向HTML元素。
例子--拷贝
下面的例子中,this被写入onclick函数里:
Javascript代码
element.onclick = doSomething
element.addEventListener('click', doSomething, false)
element.onclick = function() {this.style.color = '#cc0000';}

element.onclick = doSomething
element.addEventListener('click', doSomething, false)
element.onclick = function() {this.style.color = '#cc0000';}

例子--引用
下述情况中,this指向window:
Javascript代码
element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)

element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)

注意attachEvent()的出现。Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。因此有时不可能知道哪个HTML正在处理该事件。
组合使用
当使用内联事件注册时,你可以将this发送到函数以至于可以正常使用:
Javascript代码

function doSomething(obj) {
//this出现在event handler中并被发送到函数
//obj指向HTML元素,因此可以这样:
obj.style.color = '#cc0000';








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。

(完)

珠峰培训

广告(购买广告位)

Javascript中this关键字详解_第5张图片
Javascript中this关键字详解_第6张图片

Javascript中this关键字详解_第7张图片

留言(46条)

this平常用的时候有点混乱。不过this是遵循OO语言的,只要把这点想清楚就很好了~

引用Shimu的发言:

this平常用的时候有点混乱。不过this是遵循OO语言的,只要把这点想清楚就很好了~

记住一条:当function被作为method调用时,this指向调用对象。另外,JavaScript并不是OO的,而是object based的一种语言。之所以你会觉得this用起来混乱,是因为你还没理解JavaScript的诸如全局对象、event handler等一些机制。 所以,阮大侠写的“纯粹函数调用”其实是不“准确”的。

原来  var o = {};是在新建对象。

js看来博大精深啊.呵呵
真正学习,虽然非常讨厌javascript.没办法

JS中一切皆是对象。
是不是可以简单的理解为:
1. this指向的是被调用的对象,
2. 当子对象没有重新赋值,最终就是父对象中该属性的值。

this还可以通过call方法改变

(function(){ alert(this); }).call("hello world");

this is a troublesome 'this'

一直对javascript的this很困惑,以至于一直都尽量避免使用。现在终于理解到了。

真不错。通俗易懂~~~~~

总结的很好。看懂了。谢谢

this只能在函数内部使用,很明显不正确
this.x = 1;
alert(this.x);
这样使用this有错么?this此时指代window对象

多谢博主的文章,我觉得是我看过的最好的javascript相关资料之一。

感觉JS里一些东西真的很混乱,里面的函数即要做爸(类)又要做儿子(类实例的方法)。比如下面的代码就让人感觉无比的混乱。
var x = 2;
function test()
{
     this.x = 1;
}
var o = new test();
alert("o.x: " + o.x); //1
o.x = 3;
test();
x*=10;
  alert("o.x: " + o.x); //3
alert("x: " + x); //10

上面的那个很清晰啊,o.x是o对象的属性啊,而var x=2是window对象的属性啊,
执行完 var o=new test(); 后 o.x 就是1 ;
当执行完那个 o.x=3时 o对象的x 值变为3;
test()执行后 x变为1;
x*=10;执行后 x 变为10;改变的是全局对象的x值;
其他的o对象的x值不变;
这个主要是 把test()函数当做构造函数和一般函数的区别!


楼主写的很好,通过这种简单的例子,让我更改明白的昨天困惑了很久的问题

this确实很好用啊,用JQuery改变样式的时候深刻体会到

var x = 2;

function test(){

  this.x = 1;

}

test();

alert(x); //1

这时其实x发生了改变,与原文总结有出入

其实秘密花园里面总结的不错

1.当在全部范围内使用 this,它将会指向全局对象。


2.函数调用时,这里 this 也会指向全局对象。


3.方法调用 test.foo(); 这个例子中,this 指向 test 对象。

4.调用构造函数 new foo(); 如果函数倾向于和 new 关键词一块使用,则我们称这个函数是构造函数。在函数内部,this 指向新创建的对象。

上面这些规则在Nodejs上面好像不怎么准确。
比如:
var x = 1;

function test()
{
console.log(this.x); 
}

test(); //不是1,因为nodejs没有全局这个概念,在一个js文件里,最上层的对象不是全局对象,而是对应于这个文件的module对象。所以在nodejs里面上面的规则就不适应了。

总之就是一个大原则,谁调用,this 就指向谁

原创:Javascript中的this关键字(详解)
http://hi.baidu.com/flondon/blog/item/1af6b36ffad4cafa81cb4a30.html

这些例子让人混乱
却没解释为什么
建议想看this用法的人适当参考

引用Yoya的发言:

感觉JS里一些东西真的很混乱,里面的函数即要做爸(类)又要做儿子(类实例的方法)。比如下面的代码就让人感觉无比的混乱。
var x = 2;
function test()
{
     this.x = 1;
}
var o = new test();
alert("o.x: " + o.x); //1
o.x = 3;
test();
x*=10;
  alert("o.x: " + o.x); //3
alert("x: " + x); //10

其实这里是这样的,第一个x=2是window的属性,第一个x=1,是o对象的属性,
到了o.x = 3;,明显o对象的属性x=3了,然后运行test(),这是,它等于window.test(),所以,这时test()里面的this指向的是window,所以改变的是window的x,所以window.x=1,所以x*=10就是x=1*10

引用Pitt Mak的发言:

上面这些规则在Nodejs上面好像不怎么准确。
比如:
var x = 1;

function test()
{
console.log(this.x); 
}

test(); //不是1,因为nodejs没有全局这个概念,在一个js文件里,最上层的对象不是全局对象,而是对应于这个文件的module对象。所以在nodejs里面上面的规则就不适应了。

毕竟是基于nodejs的,每个js库都有其最上层的对象,这里所指的是底层的,原生的,原始的js吧

引用丁小倪的发言:
var x = 2; function test(){   this.x = 1; } test(); alert(x); //1 这时其实x发生了改变,与原文总结有出入 其实秘密花园里面总结的不错 1.当在全部范围内使用 this,它将会指向全局对象。

2.函数调用时,这里 this 也会指向全局对象。

3.方法调用 test.foo(); 这个例子中,this 指向 test 对象。

4.调用构造函数 new foo(); 如果函数倾向于和 new 关键词一块使用,则我们称这个函数是构造函数。在函数内部,this 指向新创建的对象。

小倪 说的非常正确。this的确值得大家详细讨论。lz也参与吧,不要仅仅写了博文不参与话题的讨论!期待……

引用Andy的发言:

记住一条:当function被作为method调用时,this指向调用对象。另外,JavaScript并不是OO的,而是object based的一种语言。之所以你会觉得this用起来混乱,是因为你还没理解JavaScript的诸如全局对象、event handler等一些机制。 所以,阮大侠写的“纯粹函数调用”其实是不“准确”的。

狭义上可以说是“纯粹的函数调用”,如果要更准确,这里所谓的纯粹函数应该是window对象的一个方法,也就是文章里提到的第二点

其实就是一个作用域的问题。搞清楚了什么时候代表window下的变量,什么时候代表对象的变量,什么时候代表“纯粹函数”中的变量。就可以了。就这么简单。

楼主写的这些基础的文章,总是能一下就点明我们这些刚接触语言的初学者!谢谢!!!

其实总结起来就一句话:

this,正在使用的对象。

多发一些javascript的博客哈,解释的简洁清析 易懂,顶一个

var x = 1;
  function test(){
    this.x = 0;
  }
  test();
  alert(x);
这个结果是1,x和this.x是两个,x是local的,this.x是window的

刚好读到 《javascript 语言精粹》,调用一章,就看到博客有摘录了

my god!!困扰我多年的偏头疼,我竟然懂了!!!特地留名感谢

引用rubatong的发言:

var x = 1;
  function test(){
    this.x = 0;
  }
  test();
  alert(x);
这个结果是1,x和this.x是两个,x是local的,this.x是window的


你这个答案绝对是0,怎么可能是1呢,不要误导其它的读者啊,有很多读者是初学者的,当执行test();的时候,this就代表了window对象,这个时候与window.test()是等价的!

引用Pitt Mak的发言:

上面这些规则在Nodejs上面好像不怎么准确。
比如:
var x = 1;

function test()
{
console.log(this.x); 
}

test(); //不是1,因为nodejs没有全局这个概念,在一个js文件里,最上层的对象不是全局对象,而是对应于这个文件的module对象。所以在nodejs里面上面的规则就不适应了。

是咯
我也记得java语言精粹这么讲的,但写个小js文件,用node.js运行下,输出undefined

很好,写的深入人心啊,理解了

直接执行函数fn()时,就相当于window.fn()。

峰哥,这篇文章写的不好啊。感觉还是很模糊。
我的网站里有的内容是转载的你的,http://nodebook.info/book/view?bid=534c8f0519980e913e9be3e6

第二部分编程思想-》 第一章 面向对象编程

引用wencan的发言:

是咯
我也记得java语言精粹这么讲的,但写个小js文件,用node.js运行下,输出undefined

声明变量x的时候不加var就好

好文!

在Javascript:the good parts里的第4章有提到这几种用法,不过博主举得例子更通俗易懂!

引用tun的发言:

原来  var o = {};是在新建对象。

的确如此,var o=new Object()的另一中写法。

请问js的call方法和apply方法有什么不同呢?

‘那就是this指的是,调用函数的那个对象。’ 不准确吧 
应该是 调用函数那个对象的上下文。

引用Andy的发言:

记住一条:当function被作为method调用时,this指向调用对象。另外,JavaScript并不是OO的,而是object based的一种语言。之所以你会觉得this用起来混乱,是因为你还没理解JavaScript的诸如全局对象、event handler等一些机制。 所以,阮大侠写的“纯粹函数调用”其实是不“准确”的。

没错,阮大侠确实写的不准确

引用Yoya的发言:

感觉JS里一些东西真的很混乱,里面的函数即要做爸(类)又要做儿子(类实例的方法)。比如下面的代码就让人感觉无比的混乱。
var x = 2;
function test()
{
     this.x = 1;
}
var o = new test();
alert("o.x: " + o.x); //1
o.x = 3;
test();
x*=10;
  alert("o.x: " + o.x); //3
alert("x: " + x); //10

其实按照阮老师所解释的来看,应该是比较好理解的吧













详解JavaScript中的this

  • 反射——Java高级开发必须懂的
  • Struts2拦截器浅析
  • 使用Struts2+Hibernate开发学生信息管理功能
  • C#开发轻松入门

来源:foocoder

详解JavaScript中的this

JavaScript中的this总是让人迷惑,应该是js众所周知的坑之一。 个人也觉得js中的this不是一个好的设计,由于this晚绑定的特性,它可以是全局对象,当前对象,或者…有人甚至因为坑大而不用this。

其实如果完全掌握了this的工作原理,自然就不会走进这些坑。来看下以下这些情况中的this分别会指向什么:

1.全局代码中的this

1
alert( this ) //window

全局范围内的this将会指向全局对象,在浏览器中即使window。

2.作为单纯的函数调用

1
2
3
4
5
function fooCoder(x) {
     this .x = x;
}
fooCoder(2);
alert(x); // 全局变量x值为2

这里this指向了全局对象,即window。在严格模式中,则是undefined。

3.作为对象的方法调用

1
2
3
4
5
6
7
8
var name = "clever coder" ;
var person = {
     name : "foocoder" ,
     hello : function (sth){
         console.log( this .name + " says " + sth);
     }
}
person.hello( "hello world" );

输出 foocoder says hello world。this指向person对象,即当前对象。

4.作为构造函数

1
new FooCoder();

函数内部的this指向新创建的对象。

5.内部函数

1
2
3
4
5
6
7
8
9
10
11
var name = "clever coder" ;
var person = {
     name : "foocoder" ,
     hello : function (sth){
         var sayhello = function (sth) {
             console.log( this .name + " says " + sth);
         };
         sayhello(sth);
     }
}
person.hello( "hello world" ); //clever coder says hello world

在内部函数中,this没有按预想的绑定到外层函数对象上,而是绑定到了全局对象。这里普遍被认为是JavaScript语言的设计错误,因为没有人想让内部函数中的this指向全局对象。一般的处理方式是将this作为变量保存下来,一般约定为that或者self:

1
2
3
4
5
6
7
8
9
10
11
12
var name = "clever coder" ;
var person = {
     name : "foocoder" ,
     hello : function (sth){
         var that = this ;
         var sayhello = function (sth) {
             console.log(that.name + " says " + sth);
         };
         sayhello(sth);
     }
}
person.hello( "hello world" ); //foocoder says hello world

6.使用call和apply设置this

1
person.hello.call(person, "world" );

apply和call类似,只是后面的参数是通过一个数组传入,而不是分开传入。两者的方法定义:

1
2
call( thisArg [,arg1,arg2,… ] );  // 参数列表,arg1,arg2,...
apply(thisArg [,argArray] );     // 参数数组,argArray

两者都是将某个函数绑定到某个具体对象上使用,自然此时的this会被显式的设置为第一个参数。

简单地总结

简单地总结以上几点,可以发现,其实只有第六点是让人疑惑的。

其实就可以总结为以下几点:

1.当函数作为对象的方法调用时,this指向该对象。

2.当函数作为淡出函数调用时,this指向全局对象(严格模式时,为undefined)

3.构造函数中的this指向新创建的对象

4.嵌套函数中的this不会继承上层函数的this,如果需要,可以用一个变量保存上层函数的this。

再总结的简单点,如果在函数中使用了this,只有在该函数直接被某对象调用时,该this才指向该对象。

1
2
3
obj.foocoder();
foocoder.call(obj, ...);
foocoder.apply(obj, …);

更进一步

我们可能经常会写这样的代码:

1
$( "#some-ele" ).click = obj.handler;

如果在handler中用了this,this会绑定在obj上么?显然不是,赋值以后,函数是在回调中执行的,this会绑定到$(“#some-div”)元素上。这就需要理解函数的执行环境。本文不打算长篇赘述函数的执行环境,可以参考《javascript高级程序设计》中对执行环境和作用域链的相关介绍。这里要指出的时,理解js函数的执行环境,会更好地理解this。

那我们如何能解决回调函数绑定的问题?ES5中引入了一个新的方法,bind():

1
2
3
4
5
6
fun.bind(thisArg[, arg1[, arg2[, ...]]])
 
thisArg
当绑定函数被调用时,该参数会作为原函数运行时的 this 指向.当使用 new 操作符调用绑定函数时,该参数无效.
arg1, arg2, ...
当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数.

该方法创建一个新函数,称为绑定函数,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.

显然bind方法可以很好地解决上述问题。

1
2
$( "#some-ele" ).click(person.hello.bind(person));
//相应元素被点击时,输出foocoder says hello world

其实该方法也很容易模拟,我们看下Prototype.js中bind方法的源码:

1
2
3
4
5
6
7
Function.prototype.bind = function (){
   var fn = this , args = Array.prototype.slice.call(arguments), object = args.shift();
   return function (){
     return fn.apply(object,
       args.concat(Array.prototype.slice.call(arguments)));
   };
};

明白了么?

相信看完全文以后,this不再是坑~









你可能感兴趣的:(web前端学习)