你不知道的this

本内容来自《你不知道的JavaScript(上卷)》,做了简单的总结

this关键字是javascript最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有的函数作用域中。但是即使是非常有经验的javascript开发者也很难说出他到底指向什么。本节将分三个部分讲解javascript中的this:

  1. 为什么要使用this

  2. 两种常见的对于this的误解

  3. this到底是什么

一、为什么要使用this

在书中通过两段代码的对比来说明为什么要使用this。第一段代码如下:这段代码在不同的上下问对象(me和you)中重复使用函数identify()和speak(),不用针对不同的对象编写不同版本的函数。

function identify(){
      return this.name.toUpperCase();
  }
  function speak(){
      var greeting = "hello, I am " + identify.call(this);
      console.log(greeting);
  }
  var me = {
      name: "zhou"
  }
  var you = {
      name: "reader"
  }
  identify.call(me); //ZHOU
  identify.call(you); //READER
  speak.call(me); // hello, I am ZHOU
  speak.call(you); // hello, I am READER

如果不使用this, 这段代码该如何写呢?那就需要给identify()和speak()显示传入一个上下文对象

      function identify(cxt){
          return cxt.name.toUpperCase();
      }
      function speak(cxt){
          var greeting = "hello, I am " + identify(cxt);
          console.log(greeting);
      }
      identify(you); //READER
      speak(me); //hello, I am ZHOU

对比发现:this提供了额一种更优雅的方式来隐式“传递”一个对象引用。因为,随着你使用的模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱。因此,通过使用this可以将API设计的更加简洁并且易于复用。

二、两种常见的对于this的误解

误解1.指向函数自身

把this理解为指向函数自身,这个推断从英语语法角度是说的通的。
常见的在函数内部引用自身的情况有:递归或者是一个在第一次被调用后自己接触绑定的事件处理器。 JavaScript的新手开发者(比如说我)通常认为:既然可以把函数看作一个对象,那就可以在调用函数时存储状态(属性的值)。
现在我们来分析这个模式,让大家看到this并不像所想的那样指向函数本身。下面这段代码,我们想要记录函数foo被调用的次数:

      function foo(num){
          console.log("foo: " + num);
          this.count++; //记录foo被调用的次数
      }
      foo.count = 0;
      
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i)
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      
       console.log(foo.count); // 0  为什么会是0呢?

foo()函数中的console.log语句产生了4条输出,证明foo()确实被调用了4次,但foo.count仍然是0,所以,仅从字面上来理解,this指向函数自身是错误的。那么,问题的原因是什么呢?
foo()函数是在全局作用域下执行的,this在这段代码中其实指向window,并且这段代码在无意中创建了一个全局变量count,他的值为NaN。
那么,遇到这样的问题许多的开发者(包括我),不会深入的思考为什么this的行为和预期的不一致,也不会回答那些很难解决,但非常重要的问题。这里提供了三种解决这个问题的方法,其中前两种方法回避了this的含义和工作原理。代码如下:

方法一 运用作用域(词法作用域)方法,该方法解决了我们遇到的问题,但是却没有直面this。

     function foo(num){
          console.log("foo: " + num);
          data.count++; //记录foo被调用的次数
      }
      var data ={
       count: 0
      };
      
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i);
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(data.count);// 4

方法二 创建一个指向函数对象的词法标识符(变量)来引用它。同样该方法仍旧回避了this的问题。

    function foo(num){
          console.log("foo: " + num);
          foo.count++; // foo指向它自身
      }
      foo.count = 0;
      for(var i=0; i<10; i++){
          if(i > 5){
              foo(i);
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(foo.count);// 4

方法三 既然我们知道this在foo函数执行时指向了别处,那么我们需要做的就是强制this指向foo函数.

function foo(num){
          console.log("foo: " + num);
          this.count++;
      }
      foo.count = 0;
      for(var i=0; i<10; i++){
          if(i > 5){
              foo.call(foo, i); //使用call()可以确保this指向函数本身
          }
      }
       // foo: 6
       // foo: 7
       // foo: 8
       // foo: 9
      console.log(foo.count);// 4

这次我们从this的角度解决了问题。

误解2.指向函数作用域

第二种常见的误解是:this指向函数作用域。这个问题有点复杂,因为在某种情况下它是正确的,但在其他情况下他却是错误的
但一定要明白,this在任何情况下都不指向函数的作用域,在javascript内部作用域和对象确实很相似,可见的标识符都是他的属性,但作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部
在文中给出了这样一段代码:

    function foo(){
         var a = 2;
         this.bar();
    }
    function bar(){
        console.log(this.a);
    }
    foo(); // ReferenceError: a is not defined

这段代码试图通过this联通foo()和bar()的词法作用域,从而让bar()可以访问foo()作用域的变量a。but it's impossible!

三、this到底是个什么玩意?

通过排除以上种种的误解,我们可以得出以下结论:

  1. this是在运行时进行绑定的,并不是在编写时绑定的

  2. this的绑定和函数声明的位置没有关系,只取决于函数的调用方式

具体细节是:当一个函数被调用时,会创建一个活动记录(也称执行上下文(context))。这个记录会包含一些信息,比如: 函数在哪里被调用(调用栈), 函数的调用方式, 传入的参数等,而this就是这个记录的一个属性,会在函数执行过程中被用到。

四、总结

  1. 随着你使用的模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱。因此,通过使用this隐式传递可以将API设计的更加简洁并且易于复用

  2. this既不指向函数自身,也不指向函数的作用域

  3. this实际上是函数被调用时发生的绑定,它的指向完全取决于函数在哪里被调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值