zoukankan      html  css  js  c++  java
  • 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的指向。
  • 相关阅读:
    五分钟搭建起一个包含CRUD功能的JqGrid表格
    TDD学习笔记【六】一Unit Test
    CQRS
    开源一个vue2的tree组件
    权限管理[Linux]
    文件管理[Linux]
    查看文本[Linux]
    常用命令[Linux]
    文件管理[Linux]
    状态机工作流
  • 原文地址:https://www.cnblogs.com/sunny_z/p/7113333.html
Copyright © 2011-2022 走看看