javascript之this的深入学习

首先,this总是返回一个对象,简单说,就是返回属性或方法“当前”所在的对象。

    由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。
  
    总结一下,JavaScript语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是这个对象(环境)。这本来并不会让用户糊涂,但是JavaScript支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象,这才是最让初学者感到困惑的地方。

    如果一个函数在全局环境中运行,那么this就是指顶层对象(浏览器中为window对象)。可以近似地认为,this是所有函数运行时的一个隐藏参数,指向函数的运行环境。

    this的使用可以分成以下几个场合:

    1)在全局环境使用this,它指的就是顶层对象window。

    this === window // true

    function f() {
         console.log(this === window); // true
    }
    
    上面代码说明,不管是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window。
  
     2)构造函数中的this,指的是实例对象。

    var Obj = function (p) {
          this.p = p;
    };

    Obj.prototype.m = function() {
           return this.p;
    };

    var o = new Obj('Hello World!');

    o.p // "Hello World!"
    o.m() // "Hello World!"

   上面代码定义了一个构造函数Obj。由于this指向实例对象,所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性;然后m方法可以返回这个p属性。

    3)对象的方法中的this

    当A对象的方法被赋予B对象,该方法中的this就从指向A对象变成了指向B对象。所以要特别小心,将某个对象的方法赋值给另一个对象,会改变this的指向。

    var obj ={
          foo: function () {
                 console.log(this);
          }
    };

    obj.foo() // obj

    上面代码中,obj.foo方法执行时,它内部的this指向obj。

    但是,只有这一种用法(直接在obj对象上调用foo方法),this指向obj;其他用法时,this都指向代码块当前所在对象(浏览器为window对象)。

    // 情况一
    (obj.foo = obj.foo)() // window

    // 情况二
    (false || obj.foo)() // window

    // 情况三
    (1, obj.foo)() // window

    上面代码中,obj.foo先运算再执行,即使它的值根本没有变化,this也不再指向obj了。

    可以这样理解,在JavaScript引擎内部,obj和obj.foo储存在两个内存地址,简称为M1和M2。只有obj.foo()这样调用时,是从M1调用M2,因此this指向obj。但是,上面三种情况,都是直接取出M2进行运算,然后就在全局环境执行运算结果(还是M2),因此this指向全局环境。
   上面三种情况等同于下面的代码。

   // 情况一
   (obj.foo = function () {
          console.log(this);
   })()

   // 情况二
   (false || function () {
         console.log(this);
   })()

  // 情况三
  (1, function () {
        console.log(this);
  })()

  同样的,如果某个方法位于多层对象的内部,这时为了简化书写,把该方法赋值给一个变量,往往会得到意料之外的结果。

  var a = {
      b: {
           m: function() {
                console.log(this.p);
          },
          p: 'Hello'
      }
  };

 var hello = a.b.m;
 hello() // undefined

 上面代码中,m是多层对象内部的一个方法。为求简便,将其赋值给hello变量,结果调用时,this指向了顶层对象。为了避免这个问题,可以只将m所在的对象赋值给hello,这样调用时,this的指向就不会变。

  var hello = a.b;
  hello.m() // Hello

   4)在Node中,this的指向又分成两种情况。全局环境中,this指向全局对象global;模块环境中,this指向module.exports。

   // 全局环境
  this === global // true

  // 模块环境
  this === module.exports // true

  this的使用应注意一下几点:

  1)避免多层this

  由于this的指向是不确定的,所以切勿在函数中包含多层的this。

  var o = {
      f1: function () {
         console.log(this);
         var f2 = function () {
            console.log(this);
         }();
     }
  }

  o.f1()
 // Object
 // Window

 上面代码包含两层this,结果运行后,第一层指向该对象,第二层指向全局对象。实际执行的是下面的代码。

  var temp = function () {
        console.log(this);
  };

  var o = {
        f1: function () {
             console.log(this);
             var f2 = temp();
        }
  }

  一个解决方法是在第二层改用一个指向外层this的变量。

  var o = {
        f1: function() {
             console.log(this);
             var that = this;
             var f2 = function() {
                   console.log(that);
             }();
        }
   }

   o.f1()
   // Object
   // Object

   上面代码定义了变量that,固定指向外层的this,然后在内层使用that,就不会发生this指向的改变。

   事实上,使用一个变量固定this的值,然后内层函数调用这个变量,是非常常见的做法,有大量应用,请务必掌握。

   2)避免数组处理方法中的this

   数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

   var o = {
         v: 'hello',
         p: [ 'a1', 'a2' ],
         f: function f() {
             this.p.forEach(function (item) {
                 console.log(this.v + ' ' + item);
             });
         } 
   }

  o.f()
  // undefined a1
  // undefined a2

   foreach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而指向顶层对象。

   解决这个问题的一种方法,是使用中间变量。

   var o = {
         v: 'hello',
         p: [ 'a1', 'a2' ],
         f: function f() {
             var that = this;
             this.p.forEach(function (item) {
                  console.log(that.v+' '+item);
             });
         }
   }
   o.f()
   // hello a1
   // hello a2

   另一种方法是将this当作foreach方法的第二个参数,固定它的运行环境。

   var o = {
         v: 'hello',
         p: [ 'a1', 'a2' ],
         f: function f() {
              this.p.forEach(function (item) {
                    console.log(this.v + ' ' + item);
              }, this);
         }
   }

   o.f()
  // hello a1
  // hello a2

  3)避免回调函数中的this

   回调函数中的this往往会改变指向,最好避免使用。

   可以采用下面的一些方法对this进行绑定,也就是使得this固定指向某个对象,减少不确定性。

   绑定 this 的方法:

   JavaScript提供了call、apply、bind这三个方法,来切换/固定this的指向。

转载于:https://www.cnblogs.com/sunny_z/p/7113333.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值