详解javascript中的this的指向问题

  • 2019 年 10 月 3 日
  • 笔记

首先,要明白this 既不指向函数自身,也不指函数的词法作用域。this一般存在于函数中,表示当前函数的执行上下文,如果函数没有执行,那么this没有内容,只有函数在执行后this才有绑定。

然后,我们来看看this四种绑定规则,也可以说在四种不同函数执行方式时this的指向。

1.默认绑定(执行)

默认执行:即没有其他绑定规则存在时的默认规则。这也是函数调用中最常用的规则。this指向window,严格模式下指向undefined

function fn(){          console.log(this)      }      fn()

fn()打印到控制台结果为window

开启严格模式后

 function fn(){          "use strict"          console.log(this)      }      fn()

fn()打印到控制台结果为undefined

2.隐式绑定(执行)

通过对象执行(通过上下文对象执行) obj.fn():当前的执行对象

function fn(){              console.log(this.a)          }          var a = 10;          var obj = {              a:20,              b:fn          }          var obj2 = {              a:30,              b:obj.b          }          obj2.b();    //??

obj2.b(); 打印到控制台结果为30

因为obj2.b();在上述代码中等价于obj2.fn(),所以此时this指向obj2,所以打印结果为30;

多层调用

  function fn(){              console.log(this.a)          }          var a = 10;          var obj = {              a:20,              b:fn          }          var obj2 = {              a:30,              b:obj.b          }          var obj3 = {              a:40,              b:obj2          }          obj3.b.b()    //?

obj3.b.b() 打印结果为30

上述代码调用结果为obj3.b->obj2,obj2.b->obj.b->fn,因为this只获取最近一层调用的上下文对象,即obj2

所以结果为30

隐式丢失(函数别名)

注意:这里存在一个陷阱,大家在分析调用过程时,要特别小心

function fn() {      console.log( this.a );  }    var a = 2;    var obj = {      a: 3,      b: fn  };    var bar = obj.b;  bar(); //?

bar() 打印结果为2

为什么会这样,obj.b 赋值给bar,那调用bar()为什么没有触发隐式绑定,使用的是默认绑定呢。

这里有个概念要理解清楚,obj.b 是引用属性,赋值给bar的实际上就是fn函数(即:bar指向fn本身)。

那么,实际的调用关系是:通过bar找到fn函数,进行调用。整个调用过程并没有obj的参数,所以是默认绑定,全局属性a。

隐士丢失(回调函数)

function fn(){              console.log(this.a)          }          var a=20;          var obj = {              a:20,              b:fn          }  setTimeout(obj.b, 2000);  function setTimeout(cb,t){              // t              cb()     //?  }

cb()打印结果为20

同样的道理,虽然参传是obj.fn,因为是引用关系,所以传参实际上传的就是fn对象本身的引用。对于setTimeout的调用,还是 setTimeout -> 获取参数中fn的引用参数 -> 执行 fn 函数,中间没有obj的参与。这里依旧进行的是默认绑定。

3.显示绑定(执行)

 function fn(){              console.log(this.a)          }           var obj1= {              a:10          }         var  obj2 ={              a:20          }         var obj3 ={              a:30          }        fn.bind(obj1)();        fn.call(obj2);        fn.apply(obj3);

fn.bind(obj1)();打印结果为10

fn.call(obj2); 打印结果为20

fn.apply(obj3);打印结果为30

这里要注意fn.bind(obj1)后面还要加一个括号才执行!

4.new 绑定(构造函数执行(通过new执行))

js中的new操作符,和其他语言中(如JAVA)的new机制是不一样的。js中,它就是一个普通函数调用,只是被new修饰了而已。

使用new来调用函数,会自动执行如下操作:

如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
从这点可以看出,this指向的就是对象本身。

function foo(a) {      this.a = a;  }    var a = 2;    var bar1 = new foo(3);  console.log(bar1.a); // ?    var bar2 = new foo(4);  console.log(bar2.a); // ?  

因为每次调用生成的是全新的对象,该对象又会自动绑定到this上,所以打印结果分别为3,4

绑定规则优先级

优先级是这样的,以按照下面的顺序来进行判断:
数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是 指定的对象。
数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。