JavaScript中的this

266 阅读4分钟

定义

简单的说this就是属性或方法运行时,当前所在的对象。和运行时所处的环境相关,而与编写代码时所在位置无关(ES5)。

  • 常规操作(1)
    //最常见的用法
var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

this属于方法describe,而方法describe又属于对A,所以this是指向的对象A,在这个时候this.name等同于a.name

  • 常规操作(2)
function f() {
  return '姓名:'+ this.name;
}

var A = {
  name: '张三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};
var C = {
  name: '赵六',
  describe: function{
    return '姓名:'+ this.name;
    }
};
var name='王五'
var f2=C.describe
A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
f()          // "姓名:王五"
f2()         //"姓名: 赵六"

由于this指向是根据环境变化的,所以A.describe()方法B.describe()输出的结果会不同,因为f方法的所在的对象变了。所以this指向改变了(注意: nodejs和浏览器环境下,this所指向的全局对象不一样,所以输出结果会不同。nodejs中f() 输出的为undefined,话说这个坑困扰了我很久!!

常见的坑

由于this只针对的指向不明确,所以会经常有各种坑。下面来列举一下几个常见的:

  • 函数自执行的时候,this指向的事全局对象Window。先看一个复杂点的例子:
var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}

o.f1()
// Object (f1中输出的事对象o)
// Window (f2中输出的是全局对象Window)

上述情况中this的指向,与我们想要的结果明显不符,上述代码实际执行的为:

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

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

函数自执行的定义:函数名+()的调用方式叫做自执行。 上面的temp()就是函数名加括号的调用方式,所以是自执行

    var f2 = function () {
      console.log(this);
    }();
    
    //上述代码实际上可以转换为:
      var f2 = function () {
      console.log(this);
    };
    f2();       //自执行

那么问题来了,为什么上述的o.f1()的时候,f1中的this指向的是对象o而不是全局对象window呢?

答案在于方法和函数的区别,o.f1()表示的是调用的对象o中的方法,而f2()则表示的是调用函数f2(),它们最大的区别,就是有没有通过对象来调用。

知道区别了,接下来看看其他例子:

   function fn(){
     	console.log(this); //Window
   }
    fn(); //最常见的自执行,函数加括号。以前只知道是指向全局,现在知道为什么了
    

ES6箭头函数中的this

箭头函数是ES6中新增的一种简洁的函数书写方式,基本语法是:参数 => 函数体。 之所以把箭头函数单独拿出来说,是因为箭头函数与我们常见的函数,有很多不同之处,this的指向就是区别之一。

箭头函数this的定义:箭头函数的中没有this,所以箭头函数中的this是直接继承(或者说调用)父级的this,并且箭头函数的this是在定义时绑定,所以和运行环境无关,这点和普通函数相反。

  • 箭头函数this的例子:
 var x = 11;
   var obj = {
   x: 22,
   say: () => {
   console.log(this.x);
   },
   say2: function () {
   console.log(this.x);
   }
   }
   obj.say();      //输出11
   obj.say2();     //输出22
   
//1.因为继承的是父级的this,而父级的this指向的是全局,所以say()拿到的数据是11
//2.第二个普通方法,所以拿到的是x=22

setTimeout的this

先举个常见的栗子:

var Person = {
    'age': 18,
    'sayHello': function () {
        setTimeout(function () {
        console.log(this.age);
        });
    }
};
var age = 20;
Person.sayHello();  // 20

var Person1 = {
    'age': 18,
    'sayHello': function () {
      setTimeout(()=>{
        console.log(this.age);
      });
    }
};
var age = 20;
Person1.sayHello();  // 18

Person1.sayHello();还比较好理解,因为使用了箭头函数,所以this继承自sayHello对象中this,而sayHello对象的this指向的是Person1对象。所以Person1.sayHello()中的this.age实际上是Person1.age。

那么为什么Person.sayHello()console.log(this.age)输出的是20呢? 答案是由setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 this 关键字在非严格模式会指向 window (或全局)对象,严格模式下为 undefined,这和所期望的this的值是不一样的。