• javascript中this关键字详解


    javascript中this关键字详解

    0.目录

    不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法。强烈推荐。

    以下篇幅有点长,希望读者耐心阅读。

    以下内容会分为如下部分:

    1.涵义

      1.1:this涵义

      1.2:this指向的可变性

    2.使用场合

      2.1:全局环境

      2.2:构造函数

      2.3:对象的方法

    3.使用注意点

      3.1:避免多层嵌套this

      3.2:避免数组处理方法中的this

      3.3:避免回调函数中的this

    1.涵义

    1.1:this涵义

    在我写的一篇关于 构造函数与new关键字 的关系的博文中谈及,new关键字总是会返回一个对象。这个对象可以是new调用构造函数时返回的空对象,也可以是在构造函数中使用return语句返回的复杂数据类型(包括对象,数组等)。

    同样,与new关键字相同,this关键字总是返回一个对象。再说的详细一些,就是属性或方法“当前”所在的对象。

    1
    2
    3
    4
    5
    6
    7
    var Keith = {
      firstName: 'Chou',
      describe: function() {
       return this.firstName;
      }
     };
    console.log(Keith.describe()); //'Chou'

    上面代码中,this.firstName表示describe方法当前所在对象的firstName属性。也就是说,在全局作用域下调用describe方法时,describe方法所在的当前对象是Keith,所以就是调用Keith.firstName。

    1.2:this指向的可变性

    由于对象的属性可以赋给另外一个对象,所以属性所在的当前对象是可变的。也就是说,this的指向是可变的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var Keith = {
      firstName: 'Chou',
      describe: function() {
       return this.firstName;
      }
     };
     var Rascal={
      firstName: 'King'
     }
     Rascal.describe=Keith.describe;
     console.log(Rascal.describe()); //'King'

    上面代码中,Keith对象中的describe属性赋给Rascal,于是describe方法中当前所在的对象就是Rascal,所以this.firstName就指向Rascal。因为是传址传递,所以修改firstName会对原对象有影响。这个例子可能不便于理解,再看看下面这个例子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function f(){
      return this.firstName;
     }
     var Keith = {
      firstName: 'Chou',
      describe:f
     };
     var Rascal={
      firstName: 'King',
      describe:f
     }
     console.log(Keith.describe()); //'Chou'
     console.log(Rascal.describe()); //'King'

    上面代码中,把方法移到全局作用域下,函数f内部使用了this关键字。随着f所在的对象不同,this指向也就不同。

    在全局作用域下this关键字会指向顶层对象(也就是window对象)。

    1
    2
    3
    4
    5
    6
    var name='keith';
     function person(){
      var name='rascal';
      return this.name;
     }
    console.log(person()); //'keith'

    上面代码中,返回的是keith而不是rascal。原因在于this指向的是全局作用域。在全局作用域中定义一个函数,默认是指向window对象,而不是函数本身。但是,如果在函数内部不使用var来声明一个局部变量,那结果也会不同。

    1
    2
    3
    4
    5
    6
    var name='keith';
     function person(){
      name='rascal';
      return this.name;
     }
    console.log(person()); //'rascal'

    上面代码中,在函数内部没有使用var来声明一个局部变量,那么此时函数内部的name属性不是局部变量,而是全局变量。所以会覆盖掉前面的name属性。如果对于局部变量和全局变量不了解,可以访问 这篇文章 。 

    只要函数被赋给另外一个变量,this的指向会发生改变。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var Keith={
      name:'keith',
      describe:function(){
       return this.name;
      }
     }
    var name='rascal';
    var f=Keith.describe;
    console.log(f()) //'rascal'

    上面代码中,返回的是rascal,而不是keith。因为Keith.describe被赋值给了f变量,而全局作用域下有一个name变量,所以Keith内部的函数的this指向就会指向f运行时所在的对象(顶层对象,也就是window对象)

    总结一下:

    1.javascript语言中,一切皆为对象(除了 undefined 和 null 之外),运行环境也是对象,所以函数都是在某个对象之中运行,this就是这个对象(环境)。

    2.this的指向是动态的。如果函数在全局作用域中,那么this就会指向全局环境;如果函数位于某个对象中,那么this就会指向该对象。

    2.使用场合

    this的使用场合可以分为以下几个场合。

    2.1:全局环境(全局作用域)

    在全局作用域中使用this对象,它指向的就是顶层对象,也就是window对象。

    1
    2
    3
    4
    function keith() {
      return (this === window)
      }
    console.log(keith()) //true

    上面代码中,不管是不是在函数内部,只要在全局作用域下运行,this就是指向顶层对象window。

    2.2:构造函数

    构造函数中的this,指向的是将要创建的对象实例。

    1
    2
    3
    4
    5
    function Keith() {
      this.sex = 'boy';
     }
     var person = new Keith();
     console.log(person.sex); //'boy'

    上面代码中,在全局作用域下定义了Keith构造函数,然后调用构造函数并赋值给person对象实例。

    构造函数创建的三个基本要求:函数名首字母大写;构造函数内部使用this关键字来指向即将生成的对象实例;使用new关键字来调用构造函数并返回对象实例。

    如果想更进一步深入了解构造函数与new关键字的关系,请移步至 这篇文章 。

    2.3:对象的方法

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var keith = {
      sex: 'boy',
      foo: function() {
       return this.sex;
      }
     };
     var rascal = {
      sex: 'girl'
     };
     rascal.foo = keith.foo;
     console.log(keith.foo()); //'boy'
     console.log(rascal.foo()); //'girl'

    上面代码中,把keith的foo函数赋值给了rascal,那么this的指向就从keith变成了rascal。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var a = {
      b: {
       p: 'keith',
       c: function() {
        return this.p;
       }
      }
     };
     var person = a.b.c;
     console.log(person()); //undefined

    上面代码中,c是两层对象里面的一个方法。为求简便,将其赋值给全局变量person,结果调用时,this指向了顶层对象window。而在window中变量p默认值为undefined。

    要解决这个问题,可以只将c所在的对象赋值给person变量,或者是直接调用。

    1
    2
    3
    var person = a.b;
    console.log(person.c()); //'keith'
    console.log(a.b.c()); //'keith'

    3.使用注意点

    3.1:避免多层嵌套this

    当在闭包中使用多层this,则this都会指向window。

    1
    2
    3
    4
    5
    6
    7
    8
    function keith() {
      console.log(this);
      return function() {
       return this;
      }
     }
     keith(); //window
     keith()(); //window

    上面代码中,在一个函数中返回另外一个匿名函数是闭包的特点之一,可以看出,当在闭包中使用this对象都会指向全局作用域中的window对象。

    如果在函数外包含一个对象,则内部this指向全局作用域,而外部this对象指向当前作用域。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var o = {
      f1: function() {
       console.log(this);
       (function() {
        console.log(this)
       })();
      }
     };
     o.f1(); //Object , Window

    上面代码包含两层this,结果运行后,第一层指向当前对象,第二层指向全局对象。

    实际执行的是如下代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function keith() {
      console.log(this);
     }
     var o = {
      f1: function() {
       console.log(this);
       var f2 = keith();
      }
     };
     o.f1(); //Object , Window

    要实现多层this嵌套,有两种解决方法:

    一是在第二层中改用一个指向外层this的变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var o = {
      f1: function() {
       console.log(this);
       var that = this;
       (function() {
        console.log(that);
       })();
      }
     };
     o.f1(); //Object , Object

    上面代码中,定义了局部变量that,固定指向了外层的this,然后在内层中使用that,就不会发生this指向的改变。但是如果函数外部内有嵌套一个对象,this还是会指向全局。

    二是Javascript中的严格模式。在严格模式下,如果内部函数的this指向了window对象,就会报错。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var a = {
      count: 0,
      fun: function() {
       'use strict';
       return this.count++;
      }
     }
     var f = a.fun;
     console.log(f()) //'TypeError: this is undefined'

    上面代码中,fun方法使用严格模式声明。把a对象中的fun方法赋值给全局变量f,那么this此时指向window对象,在严格模式下,就会报错。如果函数外部没有嵌套一个对象,那么不会报错,而是会返回undefined。

    3.2:避免数组处理方法中的this

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var keith = {
      a: 'Hello',
      b: ['b1', 'b2'],
      c: function() {
       this.b.forEach(function(item) {
        console.log(this.a + ' ' + item);
       })
      }
     };
     keith.c();
     //undefined b1
     //undefined b2

    上面代码中,forEach方法的回调函数中的this,其实指向的是window对象,因此取不到keith.a的值,同上也属于避免多层嵌套this。也就是说,内层的this不指向外部函数,而是指向顶层对象。

    要解决这个方法,可以使用that变量来代替回调函数中的this。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var keith = {
      a: 'Hello',
      b: ['b1', 'b2'],
      c: function() {
       var that = this;
       this.b.forEach(function(item) {
        console.log(that.a + ' ' + item);
       })
      }
     };
     keith.c();
     //Hello b1
     //Hello b2

    另外一种方法,就是让this做为forEach方法的第二个参数,来固定它的运行环境。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var keith = {
      a: 'Hello',
      b: ['b1', 'b2'],
      c: function() {
       this.b.forEach(function(item) {
        console.log(this.a + ' ' + item);
       }, this)
      }
     };
     keith.c();
     //Hello b1
     //Hello b2

    3.3:避免回调函数中的this

    回调函数中的this往往会改变指向。

    1
    2
    3
    4
    5
    6
    var o = {
      f: function() {
       console.log(this === o);
      }
     };
     o.f(); // true;

    上面代码中,调用o对象的f方法,返回true。

    但是,如果将f方法指定给某个按钮的click事件,this的指向就变了。

    $('button').on('click',o.f);

    上面代码中,使用了jquery方法来获取button元素,并绑定click事件。点击按钮后控制台会显示false。原因是此时this不再指向o对象了,而是指向按钮的DOM对象,因为f方法是在按钮对象的环境中被调用的。

    总结一下:

    a:如果想要多层嵌套this关键字,最常用的解决方法就是使用that变量,固定指向外层的this,然后在内层中使用that变量。就不会发生内层this指向全局的问题。

    b:如果在回调函数中使用this关键字,注意this的指向问题。

  • 相关阅读:
    LR杂记-nmon+analyser监控linux系统资源
    accept函数
    android performClick使用
    #line 的作用是改变当前行数和文件名称
    C++常用排序法、随机数
    C语言运算符优先级及结合性
    如何高效把一字节的位对换, bit0和bit7,bit1和bit6,以此类推.
    NumPy
    Rational Rose、PowerDesign、Visio的一些比较
    vld,Bounds Checker,memwatch,mtrace,valgrind,debug_new几种内存泄露检测工具的比较,Valgrind Cheatsheet
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/8896529.html
Copyright © 2020-2023  润新知