javascript弃坑之路之原来是这样的this

760 阅读6分钟

说this是js编程中最重要的变量也不为过了,而在遇到this时,也曾经傻傻分不清楚this指代的到底是什么,所以现在对曾踩过this的坑做一下总结(注:本文中的this值适用场景为执行环境为浏览器时,执行环境为node时会有不同表现)。

this的场景

虽然this很令人迷惑,但其实总会发现,在浏览器环境中,this的指代不外乎两种,要么是window,要么是某个特定的对象(window之外的对象),而涉及到具体的场景中this的取值,可以归纳为五种。

全局作用域中直接调用this

var global=4;
console.log(this.global);

如上例中,在全局作用域下定义了变量global,同样的,当我们调用this.global时,会把之前定义的变量打印出来。这是因为,全局作用域中定义的变量相当于为window对象添加了属性,而全局作用域下直接调用this,同样指代了window对象,因此能够取到之前定义的this值。

对象方法中的this

对象方法,也即对象的成员函数,对象方法中的this指代的是对象本身。

var obj={
  value:2,
  func:function(){
    console.log(this.value);//2
  }
}

如上例中obj是一个对象,value和func分别为它的成员变量和成员方法,此时在方法中取到的this.value值即为obj.value值,this指代的是obj本身。

new操作符中的this

new操作符通常和构造函数共同出现,往往用于创建一个新对象,而此时构造函数中的this指代的正是所创建的新对象。

function Car(){
 this.color="red";
 this.size="large";
 this.getColor=function(){
   console.log(this.color)
 }
}

var car=new Car();
console.log(car.getColor())//red

如上例所示,这里可以正确的把red打印出来,其中函数Car是一个最基本的构造函数,当用new 操作符来创建一个新的Color对象时,构造函数执行过程中首先会新建一个对象,然后将this赋值给这个新对象,之后所有对this的赋值操作都等同于对新对象赋值了。

普通函数中的this

在调用普通函数表达式时,调用this,往往指代的是window对象。

var value=3;
var func=function(){
  console.log(this.value);
}
func();

如上所示,func是一个普通的指代函数的变量,在该函数中调用this,指代的是window对象。

绑定this

绑定this也是很常见的一种场景,call,apply,bind方法均用于绑定this到指定的对象。

this常见的坑

以上我们把this的相关场景分成了五种,但即使这样,也常常有令人迷惑的地方,下面来看下常见的两个题目:

  • 题目一
var obj={
 a:3;
 getValue:function(){
   return this.a;
 }
}
var func=obj.getValue;
console.log(func());

看到上例,可能会毫不犹豫的说答案当然是3了,很简单嘛,obj.getValue,所属对象是obj,里面的this指代的当然是obj了,返回的this.a当然就是obj.a,是3没问题,这时候,看了控制台的打印就会大吃一惊了,答案是undefined,这是因为var func=obj.getValue这一句赋值语句被忽略了,但这是至关重要的,经过这个赋值语句之后,this指代的不再是obj本身了,而是全局对象window,因为wiondow中不含有a变量,this.a当然是undefined了。

  • 题目二
var obj={
  a:4,
  getValue:function(){
    var innerfunc=function(){
     return this.a;
    }
    return innerfunc();
  }
}

console.log(obj.func());

看到这里,可能又会脱口而出,打印出的值是4没错,这次明显就是直接调用的obj对象的func函数,this总该指向obj了吧,但是当然了,答案依然是undefined,不要忽略,getValue函数中this是经过innerfunc调用的,而innerfunc的调用对象并不是obj,所以在执行innerfunc函数时,其中的this是指代window的,a值是undefined。

this和作用域

以上的题目二之所以迷惑我们,关键的一点是,有时候不自觉得将this和作用域关联起来,因此,不要将this和作用域等同,不要自然而然的找innerfunc中的this时,会去找其包含作用域getValue的this,因为每个函数天生就有this变量,所以某个函数本身的this与其包含作用域中的this无直接关系。

快捷判断this

有了以上的五种场景和常见的两个this题目坑,或许可以作如下总结,this的指代值只有两种:

  • 指定方法的调用对象,this是调用对象 指定方法的调用对象对象也有多种方法,向第一节提到的,直接的调用对象方法,如obj.xxx(),new构造函数中指定的调用对象为新对象,apply,call,bind方法也相当于绑定了调用对象;
  • 调用函数表达式的值或直接调用函数表达式时,this是window 直接调用函数表达式或其值的场景也有很多,如
var a=function(){
  return this.value;
}
a();//调用函数表达式的值
(function(){
  return this.value;
})();//调用函数表达式
~~function(){
  return this.value;
}();//调用函数表达式;

第一节中提到的全局作用域中的this也可以等同于在一个全局函数中调用,this,是window. 有了以上两种判断方法就会更简单了,甚至只要区分出一种场景,就能快速判断了,如只要确定this所在的函数指定(绑定)了调用对象,this就相当于该对象,否则就是window.

this这么复杂好吗

上述的两个坑只是稍微列举了一下,实际开发中this的坑真的很多,尤其对我这种小白来说,总结发现this的这么多坑的原因往往与一个特性有关,就是this的后绑定,在看到涉及this的函数定义时,往往不自觉的在这里就给this赋值了,如上节中的坑一,正因为我看到的this所在的函数是属于一个对象的,所以未理会var func=obj.getValue,变不由得将this和obj绑定了,但这种明显是错误的,由于this的后绑定,我们不应随意在看到this时就将其赋值,而应好好分析在调用时this的所指才是正解。 那么为什么this要后绑定呢,给我们带来了这么多坑,这应该设计到语言的设计问题了,这里无法深入,但毋庸置疑的是,js的极其重要的原型继承机制就是依赖于this后绑定的,想象一下this不是后绑定,后果很可怕。。。。