完全弄懂js中this指向

 完全弄懂js中this指向! 

this是什么?
定义:this是包含它的函数做为方法被调用时所属的对象。
es6

  首先,this的指向在函数定义的时候是肯定不了的,只有函数执行的时候才能肯定this到底指向谁实际上this的最终指向的是那个调用它的对象(虽然在不少状况下那样去理解不会出什么问题,可是实际上会有些特殊存在,那么接下来我会深刻的探讨这个问题。chrome

例子1:数组

function a(){
    var user = " 小明";
    console.log(this.user); //undefined
    console.log(this); //Window
}
a(); 

按照咱们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就能够证实。babel

function a(){
    var user = " 小明";
    console.log(this.user); //undefined
    console.log(this);  //Window
}
window.a();

 

例子2:app

var o = {
    user:" 小明",
    fn:function(){
        console.log(this.user);  // 小明
    }
}
o.fn();

  这里的this指向的是对象o,由于你调用这个fn是经过o.fn()执行的,那天然指向就是对象o,this的指向在函数建立的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁。函数

若是要完全的搞懂this必须看接下来的几个例子post

例子3:this

var o = {
    user:" 小明",
    fn:function(){
        console.log(this.user); // 小明
    }
}
window.o.fn();

  这段代码和上面的那段代码几乎是同样的,可是这里的this为何不是指向window,若是按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,咱们建立的变量其实是给window添加属性,因此这里能够用window点o对象。es5

  这里先不解释为何上面的那段代码this为何没有指向window,咱们再来看一段代码。spa

var o = {
    a:10, 
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //12
        }
    }
}
o.b.fn();

  这里一样也是对象o点出来的,可是一样this并无执行它,那你确定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不许确,接下来我将补充一句话,我相信你就能够完全的理解this的指向的问题。

  状况1若是一个函数中有this,可是它没有被上一级的对象所调用,那么this指向的就是window,这里须要说明在js的严格版中this指向的不是window。

  状况2若是一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

  状况3若是一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3能够证实。


接下来咱们继续看几个例子。

var o = {
    a:10,
    b:{
        // a:12,
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

尽管对象b中没有属性a,这个this指向的也是对象b,由于this只会指向它的上一级对象,无论这个对象中有没有this要的东西。

还有一种比较特殊的状况,例子4:

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();

这里this指向的是window,这句话一样相当重要this永远指向的是调用它的对象,也就是看它执行的时候是谁调用就指向谁,例子4中虽然函数fn是被对象b所引用,可是在将fn赋值给变量j的时候并无执行,因此最终指向的是window,这和例子3是不同的,例子3是直接执行了fn。

 

构造函数版this:

function Fn(){
    this.user = " 小明";
}
var a = new Fn();
console.log(a.user); // 小明

  这里之因此对象a能够点出函数Fn里面的user是由于new关键字能够改变this的指向,理解这句话能够想一想咱们的例子3,咱们这里用变量a建立了一个Fn的实例(至关于复制了一份Fn到对象a里面),此时仅仅只是建立,并无执行,而调用这个函数Fn的是对象a,那么this指向的天然是对象a,那么为何对象a中会有user,由于你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

 

当this碰到return时

function fn()  
{  
    this.user = ' 小明';  
    return {};  
}
var a = new fn;  
console.log(a.user); //undefined
function fn()  
{  
    this.user = ' 小明';  
    return function(){};
}
var a = new fn;  
console.log(a.user); //undefined
function fn()  
{  
    this.user = ' 小明';  
    return 1;
}
var a = new fn;  
console.log(a.user); // 小明
function fn()  
{  
    this.user = ' 小明';  
    return undefined;
}
var a = new fn;  
console.log(a.user); // 小明

  从这些例子中能够总结出,若是返回值是一个对象,那么this指向的就是那个返回的对象,若是返回值不是一个对象那么this仍是指向函数的实例。

function fn()  
{  
    this.user = ' 小明';  
    return undefined;
}
var a = new fn;  
console.log(a); //fn {user: " 小明"}

特殊状况null也是对象,可是在这里this仍是指向那个函数的实例。

function fn()  
{  
    this.user = ' 小明';  
    return null;
}
var a = new fn;  
console.log(a.user); // 小明

 

当this遇到call、apply、bind方法时

一、call()

  

var a = {
    user:"小明",
    fn:function(){
        console.log(this.user); //小明
    }
}
var b = a.fn;
b.call(a);

经过在call方法,给第一个参数添加要把b添加到哪一个环境中,简单来讲,this就会指向那个对象。

call方法除了第一个参数之外还能够添加多个参数,以下:

var a = {
    user:"小明",
    fn:function(e,ee){
        console.log(this.user); //小明
        console.log(e+ee); //3
    }
}
var b = a.fn;
b.call(a,1,2);

二、apply()

apply方法和call方法有些类似,它也能够改变this的指向

var a = {
    user:"小明",
    fn:function(){
        console.log(this.user); //小明
    }
}
var b = a.fn;
b.apply(a);

一样apply也能够有多个参数,可是不一样的是,第二个参数必须是一个数组,以下:

var a = {
    user:"小明",
    fn:function(e,ee){
        console.log(this.user); //小明
        console.log(e+ee); //11
    }
}
var b = a.fn;
b.apply(a,[10,1]);

//注意若是call和apply的第一个参数写的是null,那么this指向的是window对象

var a = {
    user:"小明",
    fn:function(){
        console.log(this); //Window {external: Object, chrome: Object, document: document, a: Object, speechSynthesis: SpeechSynthesis…}
    }
}
var b = a.fn;
b.apply(null);

三、bind()

bind方法和call、apply方法有些不一样,可是无论怎么说它们均可以用来改变this的指向。

先来讲说它们的不一样吧。

var a = {
    user:"小明",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
b.bind(a);

咱们发现代码没有被打印,对,这就是bind和call、apply方法的不一样,实际上bind方法返回的是一个修改事后的函数。

var a = {
    user:"小明",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
var c = b.bind(a);
console.log(c); //function() { [native code] }

那么咱们如今执行一下函数c看看,能不能打印出对象a里面的user

var a = {
    user:"小明",
    fn:function(){
        console.log(this.user); //小明
    }
}
var b = a.fn;
var c = b.bind(a);
c();

总结:call和apply都是改变上下文中的this并当即执行这个函数,bind方法可让对应的函数想何时调就何时调用,而且能够将参数在执行的时候添加,这是它们的区别,根据本身的实际状况来选择使用。

 

this遇到-定时器以及箭头函数时

使用js中的定时器(setInterval,setTimeout),很容易会遇到this指向的问题。

直接上例子:

  var name = 'my name is window';
  var obj = {
      name: 'my name is obj',
      fn: function () {
          var timer = null;
          clearInterval(timer);
          timer = setInterval(function () {
              console.log(this.name);  //my name is window
          }, 1000)
     }
 }

在这里,从this.name能够看出this的指向是window。

若是没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是由于JS的定时器方法是定义在window下的。可是平时不少场景下,都须要修改this的指向。这里总结了几种:

一、最经常使用的方法:在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this。

  var name = 'my name is window';
  var obj = {
      name: 'my name is obj',
      fn: function () {
          var that = this;
          var timer = null;
          clearInterval(timer);
          timer = setInterval(function () {
              console.log(that.name);   //my name is obj
         }, 1000)
     }
 }

在fn中加了var that = this; 回调函数中使用that代替this便可。这种方法最多见,使用也最普遍。

二、使用bind()方法(bind()为ES5的标准,低版本IE下有兼容问题,能够引入es5-shim.js解决)

bind()的做用相似call和apply,都是修改this指向。可是call和apply是修改this指向后函数会当即执行,而bind则是返回一个新的函数,它会建立一个与原来函数主体相同的新函数,新函数中的this指向传入的对象。

  var name = 'my name is window';
  var obj = {
      name: 'my name is obj',
      fn: function () {
          var timer = null;
          clearInterval(timer);
          timer = setInterval(function () {
              console.log(this.name);   //my name is obj
          }.bind(this), 1000)
     }
 }

在这里为何不能用call和apply,是由于call和apply不是返回函数,而是当即执行函数,那么,就失去了定时器的做用。

三、使用es6的箭头函数:箭头函数的最大做用就是this指向。

  var name = 'my name is window';
  var obj = {
      name: 'my name is obj',
      fn: function () {
          var timer = null;
          clearInterval(timer);
          timer = setInterval(() => {
              console.log(this.name);  //my name is obj
          }, 1000)
     }
 }

箭头函数没有本身的this,它的this继承自外部函数的做用域。因此,在该例中,定时器回调函数中的this,是继承了fn的this。固然箭头函数也有兼容问题,要是兼容低版本ie,须要使用babel编译才能够。

相关文章
相关标签/搜索