《详解Javascript 中的this指针 》,这篇文章详细解释this实现的原理。"/>

编程小贴士

给你的编程提供小点子


JavaScript中this的工作原理以及注意事项

在JavaScript中,this 的概念比较复杂。除了在面向对象编程中,this 还是随处可用的。这篇文章介绍了this 的工作原理,它会造成什么样的问题以及this 的相关例子。 要根据this 所在的位置来理解它,情况大概可以分为3种:

  1. 在函数中:this 通常是一个隐含的参数。
  2. 在函数外(顶级作用域中):在浏览器中this 指的是全局对象;在Node.js中指的是模块(module)的导出(exports)。
  3. 传递到eval()中的字符串:如果eval()是被直接调用的,this 指的是当前对象;如果eval()是被间接调用的,this 就是指全局对象。

对这几个分类,我们做了相应的测试:

  1.  在函数中的this

     函数基本可以代表JS中所有可被调用的结构,所以这是也最常见的使用this 的场景,而函数又能被子分为下列三种角色:

    • 实函数
    • 构造器
    • 方法

    1.1  在实函数中的this

    在实函数中,this 的值是取决于它所处的上下文的模式。

    • Sloppy模式:this 指的是全局对象(在浏览器中就是window)。
      1
      2
      3
      4
      function sloppyFunc() {
          console.log(this === window); // true
      }
      sloppyFunc();
    • Strict模式:this 的值是undefined。
      1
      2
      3
      4
      5
      function strictFunc() {
          'use strict';
          console.log(this === undefined); // true
      }
      strictFunc();

      this 是函数的隐含参数,所以它的值总是相同的。不过你是可以通过使用call()或者apply()的方法显示地定义好this的值的。

      1
      2
      3
      4
      5
      6
      7
      function func(arg1, arg2) {
          console.log(this); // 1
          console.log(arg1); // 2
          console.log(arg2); // 3
      }
      func.call(1, 2, 3); // (this, arg1, arg2)
      func.apply(1, [2, 3]); // (this, arrayWithArgs)

      1.2  构造器中的this

      你可以通过new 将一个函数当做一个构造器来使用。new 操作创建了一个新的对象,并将这个对象通过this 传入构造器中。

      1
      2
      3
      4
      5
      6
      var savedThis;
      function Constr() {
          savedThis = this;
      }
      var inst = new Constr();
      console.log(savedThis === inst); // true

      JS中new 操作的实现原理大概如下面的代码所示(更准确的实现请看这里,这个实现也比较复杂一些):

      1
      2
      3
      4
      5
      function newOperator(Constr, arrayWithArgs) {
          var thisValue = Object.create(Constr.prototype);
          Constr.apply(thisValue, arrayWithArgs);
          return thisValue;
      }

      1.3  方法中的this

      在方法中this 的用法更倾向于传统的面向对象语言:this 指向的接收方,也就是包含有这个方法的对象。

      1
      2
      3
      4
      5
      6
      var obj = {
          method: function () {
              console.log(this === obj); // true
          }
      }
      obj.method();
  2.  作用域中的this
    在浏览器中,作用域就是全局作用域,this 指的就是这个全局对象(就像window):

    1
    2
    3
    <script>
        console.log(this === window); // true
    </script>

    在Node.js中,你通常都是在module中执行函数的。因此,顶级作用域是个很特别的模块作用域(module scope):

    1
    2
    3
    4
    5
    6
    7
    // `global` (not `window`) refers to global object:
    console.log(Math === global.Math); // true
    // `this` doesn’t refer to the global object:
    console.log(this !== global); // true
    // `this` refers to a module’s exports:
    console.log(this === module.exports); // true
  3.  eval()中的this
    eval()可以被直接(通过调用这个函数名’eval’)或者间接(通过别的方式调用,比如call())地调用。要了解更多细节,请看这里。

    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
    // Real functions
    function sloppyFunc() {
        console.log(eval('this') === window); // true
    }
    sloppyFunc();
    function strictFunc() {
        'use strict';
        console.log(eval('this') === undefined); // true
    }
    strictFunc();
    // Constructors
    var savedThis;
    function Constr() {
        savedThis = eval('this');
    }
    var inst = new Constr();
    console.log(savedThis === inst); // true
    // Methods
    var obj = {
        method: function () {
            console.log(eval('this') === obj); // true
        }
    }
    obj.method();
  4. this有关的陷阱
    你要小心下面将介绍的3个和this 有关的陷阱。要注意,在下面的例子中,使用Strict模式(strict mode)都能提高代码的安全性。由于在实函数中,this 的值是undefined,当出现问题的时候,你会得到警告。
    4.1  忘记使用new
    如果你不是使用new来调用构造器,那其实你就是在使用一个实函数。因此this就不会是你预期的值。在Sloppy模式中,this 指向的就是window 而你将会创建全局变量:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    var p = Point(7, 5); // we forgot new!
    console.log(p === undefined); // true
    // Global variables have been created:
    console.log(x); // 7
    console.log(y); // 5

    不过如果使用的是strict模式,那你还是会得到警告(this===undefined):

    1
    2
    3
    4
    5
    6
    7
    function Point(x, y) {
        'use strict';
        this.x = x;
        this.y = y;
    }
    var p = Point(7, 5);
    // TypeError: Cannot set property 'x' of undefined

    4.2 不恰当地使用方法
    如果你直接取得一个方法的值(不是调用它),你就是把这个方法当做函数在用。当你要将一个方法当做一个参数传入一个函数或者一个调用方法中,你很可能会这么做。setTimeout()和注册事件句柄(event handlers)就是这种情况。我将会使用callIt()方法来模拟这个场景:

    1
    2
    3
    4
    /** Similar to setTimeout() and setImmediate() */
    function callIt(func) {
        func();
    }

    如果你是在Sloppy模式下将一个方法当做函数来调用,*this*指向的就是全局对象,所以之后创建的都会是全局的变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var counter = {
        count: 0,
        // Sloppy-mode method
        inc: function () {
            this.count++;
        }
    }
    callIt(counter.inc);
    // Didn’t work:
    console.log(counter.count); // 0
    // Instead, a global variable has been created
    // (NaN is result of applying ++ to undefined):
    console.log(count);  // NaN

    如果你是在Strict模式下这么做的话,this是undefined的,你还是得不到想要的结果,不过至少你会得到一句警告:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var counter = {
        count: 0,
        // Strict-mode method
        inc: function () {
            'use strict';
            this.count++;
        }
    }
    callIt(counter.inc);
    // TypeError: Cannot read property 'count' of undefined
    console.log(counter.count);

    要想得到预期的结果,可以使用bind()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var counter = {
        count: 0,
        inc: function () {
            this.count++;
        }
    }
    callIt(counter.inc.bind(counter));
    // It worked!
    console.log(counter.count); // 1

    bind()又创建了一个总是能将this的值设置为counter 的函数。

    4.3 隐藏this
    当你在方法中使用函数的时候,常常会忽略了函数是有自己的this 的。这个this 又有别于方法,因此你不能把这两个this 混在一起使用。具体的请看下面这段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var obj = {
        name: 'Jane',
        friends: [ 'Tarzan', 'Cheeta' ],
        loop: function () {
            'use strict';
            this.friends.forEach(
                function (friend) {
                    console.log(this.name+' knows '+friend);
                }
            );
        }
    };
    obj.loop();
    // TypeError: Cannot read property 'name' of undefined

    上面的例子里函数中的this.name 不能使用,因为函数的this 的值是undefined,这和方法loop()中的this 不一样。下面提供了三种思路来解决这个问题:

      • that=this,将this 赋值到一个变量上,这样就把this 显性地表现出来了(除了thatself 也是个很常见的用于存放this的变量名),之后就使用那个变量:
        1
        2
        3
        4
        5
        6
        7
        loop: function () {
            'use strict';
            var that = this;
            this.friends.forEach(function (friend) {
                console.log(that.name+' knows '+friend);
            });
        }
      • bind()。使用bind()来创建一个函数,这个函数的this 总是存有你想要传递的值(下面这个例子中,方法的this):
        1
        2
        3
        4
        5
        6
        loop: function () {
            'use strict';
            this.friends.forEach(function (friend) {
                console.log(this.name+' knows '+friend);
            }.bind(this));
        }
      • 用forEach的第二个参数。forEach的第二个参数会被传入回调函数中,作为回调函数的this 来使用。
        1
        2
        3
        4
        5
        6
        loop: function () {
            'use strict';
            this.friends.forEach(function (friend) {
                console.log(this.name+' knows '+friend);
            }, this);
        }
  5. 最佳实践

理论上,我认为实函数并没有属于自己的this,而上述的解决方案也是按照这个思想的。ECMAScript 6是用箭头函数(arrow function)来实现这个效果的,箭头函数就是没有自己的this 的函数。在这样的函数中你可以随便使用this,也不用担心有没有隐式的存在。

1
2
3
4
5
6
7
8
loop: function () {
    'use strict';
    // The parameter of forEach() is an arrow function
    this.friends.forEach(friend => {
        // `this` is loop’s `this`
        console.log(this.name+' knows '+friend);
    });
}

我不喜欢有些API把this 当做实函数的一个附加参数:

1
2
3
4
5
6
7
beforeEach(function () {  
    this.addMatchers({  
        toBeInRange: function (start, end) {  
            ...
        }  
    });  
});

把一个隐性参数写成显性地样子传入,代码会显得更好理解,而且这样和箭头函数的要求也很一致:

1
2
3
4
5
6
7
beforeEach(api => {
    api.addMatchers({
        toBeInRange(start, end) {
            ...
        }
    });
});

82 Responses to “ JavaScript中this的工作原理以及注意事项 ”

  1. see pron说道:

    ePI3aa There as definately a great deal to know about this topic. I like all the points you have made.

  2. Tattoo Shop说道:

    I think this is a real great blog post.Really thank you! Will read on

  3. Thanks for the post.Thanks Again. Really Cool.

  4. Very good post.Thanks Again. Much obliged.

  5. you have brought up a very excellent details , thanks for the post.

  6. website说道:

    Really wonderful info can be found on web site.

  7. Oluwadamilare说道:

    I?d must test with you here. Which isn at one thing I usually do! I enjoy studying a put up that will make people think. Additionally, thanks for permitting me to remark!

  8. a fantastic read说道:

    I think, that you are mistaken. Let as discuss. Write to me in PM, we will communicate.

  9. Get the facts说道:

    Thanks again for the blog article.Really looking forward to read more.

  10. w88world说道:

    Thank you for your blog article.Much thanks again. Want more.

  11. This blog is obviously interesting and informative. I have picked many useful things out of this blog. I ad love to go back every once in a while. Thanks a lot!

  12. hotel websites说道:

    Thanks a lot for the article post.Really looking forward to read more. Much obliged.

  13. Integer vehicula pulvinar risus, quis sollicitudin nisl gravida ut

  14. private Hire说道:

    Remarkable! Its actually remarkable article, I have got much clear idea regarding

  15. Hoa Tuoi Van Nam说道:

    Thank you for your article.Really thank you! Will read on

  16. My brother recommended I might like this website. He was entirely right. This post truly made my day. You cann at imagine simply how much time I had spent for this information! Thanks!

  17. The Lost Ways说道:

    Informative and precise Its difficult to find informative and precise info but here I found

  18. Your favourite reason appeared to be at the net the simplest

  19. Cardiac说道:

    I really liked your blog.Much thanks again. Will read on

  20. Learn More说道:

    Wow, awesome blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your web site is great, as well as the content!

  21. Thanks-a-mundo for the blog article.Thanks Again. Much obliged.

  22. craft supplies说道:

    plumbing can actually be a hardwork specially if you usually are not very skillfull in undertaking residence plumbing::

  23. wow, awesome post.Much thanks again. Will read on

  24. Muchos Gracias for your article.Really looking forward to read more.

  25. Souls in the Waves Superior Morning, I just stopped in to visit your site and thought I would say I experienced myself.

  26. This can be a excellent weblog and i would like to take a look at this each and every day with the week

  27. There as definately a lot to know about this issue. I like all the points you have made.

  28. Wow! This can be one particular of the most useful blogs We have ever arrive across on this subject. Basically Fantastic. I am also an expert in this topic so I can understand your effort.

  29. This is my first time pay a visit at here and i am truly pleassant to read all at alone place.

  30. Major thankies for the blog post.Thanks Again. Will read on

  31. url shortener说道:

    I use pocket money also. I love it. I also use MPG and it allows me to record my gas purchases and maintenance transactions into pocket money right from MPG.

  32. healthy说道:

    Say, you got a nice blog article.Much thanks again. Awesome.

  33. Vale Flash O PORTAL MUTIMDIA DO VALE DO PARABA

  34. Hosting说道:

    Rattling clean site, thanks due to this post.

  35. maeng da kratom说道:

    Really cool post, highly informative and professionally written..Good Job! car donation sites

  36. Tejido a crochet说道:

    It as not all on Vince. Folks about him ended up stealing his money. Also when you feel his professional career is more than, you are an idiot.

  37. local说道:

    This blog is really educating additionally amusing. I have discovered many handy tips out of this amazing blog. I ad love to come back again and again. Cheers!

  38. more说道:

    Wow! this is a great and helpful piece of info. I am glad that you shared this helpful info with us. Please stay us informed like this. Keep writing.

  39. benh gut (gout)说道:

    Useful information. Fortunate me I discovered your website accidentally, and I am surprised why this twist of fate did not took place in advance! I bookmarked it.

  40. School admission说道:

    There is certainly a great deal to learn about this subject. I really like all the points you made.

  41. School admission说道:

    Im thankful for the blog post.Thanks Again. Really Cool.

  42. Say, you got a nice article post.Really thank you! Awesome.

  43. Thanks for helping out and about, superb data. The a number of stages regarding man are generally infancy, childhood, adolescence, and obsolescence. by Bruce Barton.

  44. This is very interesting, You are a very skilled blogger. I have joined your rss feed and look forward to seeking more of your wonderful post. Also, I have shared your site in my social networks!

  45. You need to participate in a contest for top-of-the-line blogs on the web. I will suggest this web site!

  46. Is there a mint app for UK people that links into your bank? Thanks

  47. Very informative blog.Thanks Again. Awesome.

  48. This is one awesome blog article.Much thanks again. Keep writing.

  49. Very nice post and straight to the point. I don at know if this is actually the best place to ask but do you guys have any ideea where to get some professional writers? Thx

  50. Some genuinely fantastic info , Gladiolus I detected this.

  51. Wow, fantastic weblog format! How long have you been blogging for? you make running a blog look easy. The entire glance of your web site is great, let alone the content material!

  52. Very informative article.Much thanks again. Want more.

  53. Sac Lancel En Vente ??????30????????????????5??????????????? | ????????

  54. wonderful points altogether, you simply won a logo new reader. What may you recommend about your publish that you made a few days in the past? Any certain?

  55. Looking around I like to browse in various places on the internet, regularly I will go to Digg and follow thru of the best offered […]

  56. go there说道:

    Some genuinely quality content on this web site , saved to my bookmarks.

  57. Looking forward to reading more. Great article post.Much thanks again. Really Cool.

  58. visit website说道:

    pretty helpful material, overall I imagine this is worth a bookmark, thanks

  59. Very neat article.Thanks Again. Really Great.

  60. Some genuinely nice stuff on this web site , I it.

  61. check out说道:

    That is a great tip especially to those fresh to the blogosphere. Brief but very accurate info Many thanks for sharing this one. A must read article!

  62. You have made some decent points there. I checked on the web for additional information about the issue and found most people will go along with your views on this site.

  63. Isabel Marant Sneakers Pas Cher аАа’аАТ‚б‚Т€Тœ

  64. post说道:

    Keep up the superb work , I read few posts on this web site and I conceive that your web blog is rattling interesting and contains bands of great information.

  65. more information说道:

    Im obliged for the blog article. Want more.

  66. Very good blog post.Thanks Again. Really Cool.

  67. find more here说道:

    My brother recommended I might like this blog. He was totally right. This post truly made my day. You cann at imagine just how much time I had spent for this info! Thanks!

  68. click here说道:

    I value the blog post.Much thanks again. Want more.

  69. Im thankful for the blog.Thanks Again. Much obliged.

  70. caratulas CD说道:

    This very blog is no doubt awesome as well as diverting. I have found helluva helpful stuff out of this blog. I ad love to return again and again. Thanks!

  71. It as nice to definitely arrive a web internet site in which the blogger is intelligent. Thanks for creating your web internet site.

  72. copper fit说道:

    Thanks a lot for the blog post.Much thanks again. Keep writing.

  73. do vui说道:

    Thank you ever so for you article post.Really looking forward to read more. Awesome.

  74. official site说道:

    It as not that I want to replicate your web site, but I really like the layout. Could you tell me which theme are you using? Or was it tailor made?

  75. more info说道:

    Wow, fantastic blog layout! How long have you ever been blogging for? you make running a blog look easy. The entire look of your web site is great, let alone the content!

  76. magnificent issues altogether, you simply won a emblem new reader. What may you recommend in regards to your post that you just made a few days in the past? Any sure?

  77. You have brought up a very excellent details , regards for the post.

  78. Wow, this article is good, my sister is analyzing such things,

  79. Thanks-a-mundo for the article post.Thanks Again. Really Cool.

  80. sunday说道:

    Normally I do not learn article on blogs, however I wish to say that this write-up very compelled me to take a look at and do so! Your writing style has been amazed me. Thanks, quite great article.

  81. No one can reject from the quality of this video posted at this web site, nice job, keep it all the time.

  82. When Someone googles something that relates to one of my wordpress blogs how can I get it to appear on the first page of their serach results?? Thanks!.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>