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

要解决这个问题我们需要了解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

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript">
    var name = "Bob"; 
    var that = null;
    var nameObj ={ 
    name : "Tom", 
    showName : function(){ 
    alert(this.name); 
    }, 
    waitShowName : function(){
    that = this;
    setTimeout("that.showName();", 1000);
    }
    }; 

    nameObj.waitShowName();
    </script>
    </head>
    <body>

    </body>
    </html>

    这样才会弹出tom对吧~

深入浅出 JavaScript 中的 this

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

10 评论:


群锋
, 软件工程师, IBM

2012 年 7 月 09 日

  • 内容


在 IBM Bluemix 云平台上开发并部署您的下一个应用。

开始您的试用

在 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代码中(<element
onclick=”doSomething()”>), 此时this指向 window对象 。
2) Traditional event registration 传统事件注册 (DHTML方式).
形如 element.onclick = doSomething; 此时this指向 element对象
3) <element onclick=”doSomething(this)”>作为参数传递可以指向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中可以查看一些更多的信息。
 
如果我们在没有任何更多准备情况下执行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元素,并且该元素的颜色得以改变。
 
这种方法使得我们能够复制这个函数到多个event handler。每次this都将指向正确的HTML元素:
 
这样你就可以最大限度使用this。每当执行该函数,this所指向的HTML元素都正确响应事件,这些HTML元素拥有doSomething()的一个拷贝。
Referring
然而,如果你使用inline event registration(内联事件注册)
Javascript代码
<element onclick="doSomething()">
<element onclick="doSomething()">
你将不能拷贝该函数!反而这种差异是非常关键的。onclick属性并不包含实际的函数,仅仅是一个函数调用。
Javascript代码
doSomething();
doSomething();
因此,它将声明“转到doSomething()并且执行它”。当我们到达doSomething(),this关键字又重新指向了全局的window对象,函数返回错误信息。

 
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代码
<element onclick="doSomething()">
alert(element.onclick)
<element onclick="doSomething()">
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="this.sytle.color = '#cc0000';">
element.onclick = doSomething
element.addEventListener('click', doSomething, false)
element.onclick = function() {this.style.color = '#cc0000';}
<element onclick="this.sytle.color = '#cc0000';">
例子--引用
下述情况中,this指向window:
Javascript代码
element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)
<element onclick="doSomething()">
element.onclick = function() {doSomething()}
element.attachEvent('onclick', doSomething)
<element onclick="doSomething()">
注意attachEvent()的出现。Microsoft event registration model最主要的弊端是attachEvent()创建了一个指向函数的引用,而不是复制它。因此有时不可能知道哪个HTML正在处理该事件。

组合使用
当使用内联事件注册时,你可以将this发送到函数以至于可以正常使用:
Javascript代码
<element onclick="doSomething(this)">
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。

(完)

文档信息

相关文章

广告(购买广告位)



留言(46条)

Shimu 说:

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

2010年5月 1日 11:04 |  | 引用

Andy 说:

引用Shimu的发言:

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

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

2010年5月 1日 12:42 |  | 引用

tun 说:

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

2010年5月 1日 19:11 |  | 引用

mead 说:

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

2010年5月 2日 08:53 |  | 引用

天堂夜风 说:

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

2010年5月 3日 20:00 |  | 引用

netwjx 说:

this还可以通过call方法改变

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

2010年5月 5日 11:45 |  | 引用

this 说:

this is a troublesome 'this'

2010年7月 8日 19:51 |  | 引用

JerryTang 说:

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

2010年8月15日 03:24 |  | 引用

looping 说:

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

2010年11月12日 17:19 |  | 引用

wtz 说:

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

2011年4月13日 20:51 |  | 引用

黑幕困兽 说:

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

2011年5月25日 23:27 |  | 引用

tanvstan 说:

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

2011年6月16日 15:08 |  | 引用

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

2011年11月29日 11:39 |  | 引用

Mr lang 说:

上面的那个很清晰啊,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()函数当做构造函数和一般函数的区别!

2011年12月25日 13:45 |  | 引用

Denney 说:

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

2011年12月30日 11:00 |  | 引用

landuary 说:

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

2012年2月16日 11:41 |  | 引用

丁小倪 说:

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 指向新创建的对象。

2012年4月 5日 18:02 |  | 引用

Pitt Mak 说:

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

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

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

2012年5月 8日 21:05 |  | 引用

akasuna 说:

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

2012年5月 9日 14:52 |  | 引用

bing 说:

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

2012年5月14日 17:25 |  | 引用

qbq 说:

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

2012年7月 2日 11:54 |  | 引用

Hedgehog 说:

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

2012年7月 4日 18:01 |  | 引用

Hedgehog 说:

引用Pitt Mak的发言:

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

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

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

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

2012年7月 4日 18:06 |  | 引用

zhangyq 说:

引用丁小倪的发言:

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也参与吧,不要仅仅写了博文不参与话题的讨论!期待……

2013年3月 6日 11:30 |  | 引用

Fenix 说:

引用Andy的发言:

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

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

2013年5月 3日 01:29 |  | 引用

猪头 说:

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

2013年6月13日 09:54 |  | 引用

andiechu 说:

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

2013年7月16日 14:13 |  | 引用

李琨 说:

其实总结起来就一句话:

this,正在使用的对象。

2013年9月24日 10:42 |  | 引用

firststation 说:

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

2014年1月10日 10:25 |  | 引用

rubatong 说:

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

2014年3月18日 13:13 |  | 引用

FFF永夜 说:

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

2014年4月 7日 19:39 |  | 引用

guxuede 说:

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

2014年4月22日 15:24 |  | 引用

fygate 说:

引用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()是等价的!

2014年5月16日 14:19 |  | 引用

wencan 说:

引用Pitt Mak的发言:

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

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

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

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

2014年5月20日 18:32 |  | 引用

lucky 说:

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

2014年5月23日 09:46 |  | 引用

海农 说:

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

2014年6月 5日 09:36 |  | 引用

Eddy 说:

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

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

2014年7月30日 11:34 |  | 引用

Yibin 说:

引用wencan的发言:

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

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

2014年8月 2日 17:22 |  | 引用

Hiveer 说:

好文!

2014年8月20日 14:34 |  | 引用

Denzel.MFFL 说:

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

2014年11月11日 13:51 |  | 引用

justme_cq 说:

引用tun的发言:

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

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

2015年2月 1日 11:51 |  | 引用

wendy 说:

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

2015年2月26日 00:33 |  | 引用

lixiaoshenxian 说:

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

2015年3月 6日 17:16 |  | 引用

paladintyrion 说:

引用Andy的发言:

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

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

2015年3月20日 15:49 |  | 引用

fottchen 说:

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

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

2015年8月 6日 16:29 |  | 

详解JavaScript中的this

2013/05/07 · Web前端开发 · Javascript

分享到:30

来源: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不再是坑~

时间: 2024-10-30 23:51:32

Javascript中this关键字详解的相关文章

javascript中this关键字详解_javascript技巧

不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法.强烈推荐. 以下篇幅有点长,希望读者耐心阅读. 以下内容会分为如下部分: 1.涵义 1.1:this涵义 1.2:this指向的可变性 2.使用场合 2.1:全局环境 2.2:构造函数 2.3:对象的方法 3.使用注意点 3.1:避免多层嵌套this 3.2:避免数组处理方法中的this 3.3:避免回调函数中的this 1.涵义 1.1:this涵义 在我写的一篇关于 构造函数与new关键字

javascript中new关键字详解_基础知识

和其他高级语言一样javascript中也有new关键字,我们以前认知的new是用来创建一个类的实例对象,但在js中万物皆是对象,为何还要new关键字呢,其实js中new关键字不是用来创建一个类的实例对象,而是用于继承. 接下来,本文将带你一起来探索JS中new的奥秘... function Animal(name){ this.name = name; } Animal.color = "black"; Animal.prototype.say = function(){ conso

Java中final关键字详解_php技巧

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 主要介绍:一.final关键字的基本用法.二.深入理解final关键字 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能

javascript中this指向详解_基础知识

JavaScript 是一种脚本语言,支持函数式编程.闭包.基于原型的继承等高级功能.JavaScript一开始看起来感觉会很容易入门,但是随着使用的深入,你会发JavaScript其实很难掌握,有些基本概念让人匪夷所思.其中JavaScript 中的 this 关键字,就是一个比较容易混乱的概念,在不同的场景下,this会化身不同的对象.有一种观点认为,只有正确掌握了 JavaScript 中的 this 关键字,才算是迈入了 JavaScript 这门语言的门槛.在主流的面向对象的语言中(例

比较全面的C 、Java、JavaScript中的正则表达式详解_正则表达式

什么是正则表达式? 正则表达式(Regular Expression) 就是用某种模式去匹配一类字符串的公式.如你要在一篇文章中查找第一个字是"罗"最后一个字是"浩"的三个字的姓名,即"罗 * 浩":那么"罗 * 浩"就是公式,也称作 模式(Pattern) ,这篇文章就是 要匹配的串( 或叫文本 text) .再如,你要检查输入的一个字符串是否是 126 邮箱的格式,你得制定一个规则去查检,这种规则就是正则表达式. 从入门开

javascript中Function类型详解_javascript技巧

Function 类型 function类型,毋庸置疑是js中相当重要的一个玩意. 1.这玩意首先是一个对象,也就是说它是一个引用类型.陈述:一听说是对象,是不是很有一种它的基类是object对象错觉感,No, 它和object是独立的2个东西.当你typeof function 时,返回的是 funciton 并非 object 2.每个函数都是 Function 对象的一个实例,它与其他引用对象一样具有属性和方法.由于它是对象所以函数名是指向函数对象的指针 关于函数的声明的语法支持: <sc

javascript中indexOf技术详解_javascript技巧

JavaScript提供了几种技术,来在字符串中搜索一个单词.数字或其他的一串字符.搜索可能很方便,例如,如果你想要知道访问者使用哪种Web浏览器来浏览你的站点.每个Web浏览器在一个字符串中标识关于自己的信息,该字符串包含了很多不同的统计数据.可以通过在一个Web页面中添加下面这段JavaScript,并且在Web浏览器预览,从而看到这个字符串: <script> alert(navigator.userAgent): </script> Navigator是一个Web浏览器对象

javascript之this关键字详解介绍

因为在接触到它之前,大部分人认为this是那些oop语言的专利.至少我曾经是这么认为的. 随着时间的推移,对javascript的进一步提高.this那神秘的面纱才一步步被揭开.话休绕舌,下面就一起来看看这层神秘的面纱背后的this吧. 首先,我们要知道this是什么.它的含义.通俗的来说, this首先是一个对象,其次要知道的是this不是由它本身出现在何处来决定的. 而是由调用它的对象来决定的,可以简单的理解为.是谁调用了this,那么this便代表谁.比如在全局作用域中调用this.那么此

JavaScript中this绑定详解

this 可以说是 javascript 中最耐人寻味的一个特性,就像高中英语里各种时态,比如被动时态,过去时,现在时,过去进行时一样,无论弄错过多少次,下一次依然可能弄错.本文启发于<你不知道的JavaScript上卷>,对 javasript 中的 this 进行一个总结. 学习 this 的第一步就是明白 this 既不是指向函数自身也不指向函数的作用域.this 实际上是在函数被调用时发生的绑定,它指向什么地方完全取决于函数在哪里被调用. 默认绑定 在 javascript 中 ,最常