【 JavaScript 基础】之this关键字

作者: 王永迪 | 来源:发表于2016-11-30 08:39 被阅读962次

    前言

    JS 是一种脚本语言,因此被很多人认为是简单易学的。然而情况可能与之相悖,JS 遵从函数式编程、闭包、基于原型的继承等高级功能。本文介绍一下JS中的 this 关键字,可以这样说,正确掌握了 JS 中的 this 关键字,才算迈入了 JS 这门语言的门槛。

    注: 本文采用部分 es6 进行代码分析


    Java工程师所熟识的this

    在 Java 中定义类经常会使用 this 关键字,多数情况下是为了避免命名冲突,比如在下面例子的中,定义一个 Image 类,很自然的,大家会使用 width、height 为其属性或成员变量命名,在构造函数中,使用 width、height 为参数命名。无论哪种情况,this 的含义是一样的,均指当前类对象,当调用构造方法时全局变量width、height的值会被修改。

        public class Image {
            private int width = 0;
            private int height = 0;
    
            public Image(int width, int height) {
                this.width = width;
                this.height = height;
            }
    
            public void println() {
                System.out.println("width = " + this.width);
                System.out.println("height = " + this.height);
            }
    
        }
    

    调用方式如下:

            Image image = new Image(10, 10);
            image.println();
    

    输出结果:

    width = 10
    height = 10
    
    Process finished with exit code 0
    

    可以看得出来this.x 与 this.y 直接修改的全局变量的值,也证明了我们上面的观点~

    JS语言中的 this

    由于JS等脚本语言运行期进行解释的特性,JS 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。

    JS 中函数的调用有以下几种方式:

    1、作为对象方法调用
    2、作为函数调用
    3、作为构造函数调用
    4、使用 apply 或 call 调用

    接下来我们按照调用方式的不同,分别讨论 this 的含义。

    作为对象方法调用

    var image = { 
       width : 0, 
       height : 0, 
       update : function(width, height) { 
         this.width = this.width + width
         this.height = this.height + height
       } 
     }
    
     // this 绑定到当前对象,即 image 对象
     // 执行此方法后image的width与Height属性值变为1
     image.update(1, 1) 
    

    作为函数调用

    当调用函数时,此时 this 绑定到全局对象,在浏览器中,window 就是该全局对象,例如下面:setLocalValue 函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的效果~

     function setLocalValue(value) { 
       this.localValue = value
     } 
    
     setLocalValue(5)
     
     // localValue 已经声明成一个值为 5 的全局变量
     console.log(localValue) // ==> 5
    

    对于内部函数,即声明在一个函数体内的函数,这种绑定到全局对象的方式会衍生出一个新的问题,我们仍然以前面提到的 image 对象为例,这次我们希望在 update 方法内定义两个函数,分别将 width、height 属性进行修改。结果可能出乎大家意料,不仅 image 对象没有移动,反而多出两个全局变量 width、height。

    var image = { 
      width : 0, 
      height : 0, 
      update : function(width, height) { 
        // 内部函数
        var updateWidth = function(width) { 
          //this 绑定到了哪里?
          this.width = width;
        } 
        // 内部函数
        var updateHeight = function(height) { 
          //this 绑定到了哪里?
          this.height = height;
        }
        
        updateWidth(width) 
        updateHeight(height) 
      } 
    }
     
     image.update(1, 1)
     
     console.log(image.width) // ==> 0
     console.log(image.height) // ==> 0
     console.log(width) // ==> 1
     console.log(height) // ==> 1
    

    这属于 JS 语言的设计缺陷,正确的设计方式是内部函数的 this 应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,我们一般采用变量替换的方法,该变量草民习惯命名为成 self~

    var image = { 
      width : 0, 
      height : 0, 
      update : function(width, height) { 
        var self = this
        // 内部函数
        var updateWidth = function(width) { 
          self.width = width;
        } 
        // 内部函数
        var updateHeight = function(height) { 
          self.height = height;
        }
        
        updateWidth(width) 
        updateHeight(height) 
      } 
    }
     
     image.update(1, 1)
     
     console.log(image.width) // ==> 1
     console.log(image.height) // ==> 1
    

    这样就达到了我们想要的效果~

    作为构造函数调用

    JS 支持面向对象编程,但与主流的面向对象式编程语言不同。

    :es6之前JS 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式,JS 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样,作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用,如下es5示例。

    :es6之后支持了class的概念,重写constructor方法,this 绑定到新创建的对象上。

    es5示例 :

    function Image(width, height){ 
        this.width = width
        this.height = height
    }
    
    var img1 = new Image(10, 10)
    

    es6 示例 :

    class Image {
    
      constructor(width, height) {
        this.width = width
        this.height = height
      }
    
    }
    
     let img1 = new Image(10, 10)
    

    使用 apply 或 call 调用

    此次着重强调一下,在 JS 语言中函数也是对象,对象则有行为(方法),apply 和 call 就是函数对象的方法。这两个方法功能超级强大,它们允许切换函数执行的上下文环境(context),即 this 指向的对象,在很多JS库中也得到了广泛应用~

    class Image {
    
      constructor(width, height) {
        this.width = width
        this.height = height
      }
    
      update(width, height) { 
         this.width = width
         this.height = height
      } 
    
    }
    
     let img1 = new Image(0, 0)
     let img2 = {width: 0, height: 0}
     img1.update(10, 10)
     
     // img2 width、height ==> 20
     img1.update.apply(img2, [20, 20]) 
     
     // img2 width、height ==> 20
     img1.update.call(img2, 20, 20)
    

    在上面的例子中,我们使用构造函数生成了一个对象 img1,该对象同时具有 update 方法,同时定义了另一个对象 img2,使用 apply 可以将 img1 的方法应用到 img2 上,这时候 this 也被绑定到对象 img2 上。

    结语:

    JS之this关键字就介绍到这里,谢谢大家的观看,希望对大家有所帮助~

    相关文章

      网友评论

      • 就叫初九吧:scope和this一起来看好很多 感觉this最终还是scope的问题
        王永迪: @十四鱼 嗯,是的

      本文标题:【 JavaScript 基础】之this关键字

      本文链接:https://www.haomeiwen.com/subject/srgzpttx.html