深入理解JS函数中this指针的指向

https://www.cnblogs.com/zjjDaily/p/9482958.html原文链接

函数在执行时,会在函数体内部自动生成一个this指针。谁直接调用产生这个this指针的函数,this就指向谁。

怎么理解指向呢,我认为指向就是等于。例如直接在js中输入下面的等式:

console.log(this===window);//true

 情况不同,this指向的对象也不同。例如:

1.  函数声明的情况

复制代码
var bj=10;
function add(){
    var bj=20;
    console.log(this);//window
    console.log(this.bj);//10
    console.log(bj);//20
    console.log(this.bj+bj);//30
}
add();
window.add();
复制代码

(1) 执行了add()之后,此时的this指向的是window对象,为什么呢?因为这时候add是全局函数,是通过window直接调用的。所以下面我专门写了个window.add()就是为了说明,全局函数的this都是指向的window。

(2) 就像alert()自带的警告弹窗一样,window.alert()执行之后也是一样的效果。所以只要是   window点   这种调用方式都可以省略掉,因此警告弹窗可以直接使用alert()。

2.  函数表达式

复制代码
var bj=10;
var zjj=function(){
    var bj=30;
    console.log(this);//window
    console.log(this.bj);//10
    console.log(bj);//30
    console.log(this.bj+bj);//40
}
console.log(typeof zjj);//function
zjj();
window.zjj();
复制代码

(1) 执行了zjj()之后,函数中的this也是指向window对象。原因和第一个是一样的,都是通过window这个对象直接调用

 

3.  函数作为对象的属性去调用------例一

复制代码
var bj=10;
var obj={
    name:"八戒",
    age:"500",
    say:function(){
        var bj=40;
        console.log(this);//就是obj这个对象
        console.log(this.bj);//undefined
        console.log(this.name);//八戒
    }
}
obj.say();
window.obj.say();    
复制代码

(1) 当obj.say()被执行的时候,此时的this指向的是 obj 这个对象,为什么呢?因为say函数是通过obj这个对象直接调用的。

(2) 那有人可能会问了,obj对象实际上也是通过window对象调用的,为什么this不指向window呢?我认为是因为say这个函数是通过 obj 对象直接调用的,而没有通过 window 对象直接调用,因此this不会指向window。看下面的例子就明白了。

 

3.1  函数作为对象的属性去调用------例二

复制代码
var bj=10;
var obj={
    name:"八戒",
    age:500,
    say:function(){
        console.log(this);//是obj这个对象
        console.log(this.bj);//undefined
        console.log(this.name)//八戒
    },
    action:{
        name:"悟空",
        age:1000,
        say:function(){
            console.log(this);//是action这个对象
            console.log(this.bj);//undefined
            console.log(this.name)//悟空
        }
    }
}
obj.say();
obj.action.say();
window.obj.action.say();
复制代码

(1) obj.say()执行之后,此时这个函数里的this指向的是obj对象,原因是因为say函数是通过obj直接调用的。

(2) obj.action.say()执行之后,此时这个函数里的this指向的是action对象,原因是因为say函数是通过action对象直接调用的。并没有通过obj直接调用。也没有通过 window 直接调用,所以此时action对象中say函数里的的this指向并不会是obj或者window。

 

3.2  函数作为对象的属性去调用------例三

复制代码
var bj=10;
var obj={
    name:"八戒",
    age:500,
    say:function(){
        console.log(this);//就是obj这个对象
        console.log(this.bj);//undefined
        console.log(this.name)//八戒
        function wk(){
            console.log(this);//window
            console.log(this.bj);//10
            console.log(this.name);//这里显示的是为空
        }
        wk();        
    },
}
obj.say();
复制代码

(1) 这种情况下,say函数里的this指针还是指向的obj,原因是因为say函数是通过obj直接调用

(2) 但是这时候wk函数中的this就是指向的是window了。为什么呢?因为 wk()函数在 say()函数中,是属于普通函数调用,但是并没有通过say或者obj直接调用,只是自执行,这个时候,wk就是一个全局函数,因此该函数的this指向的就是window。

(3) 那为什么this.name是显示的为空呢?因为 window 对象中本身就有一个 name 值,并不是某处添加的,如果把name换成age,得到的就是undefined了。

(4) 那怎样让wk()函数中的this指向obj呢。一种方式就是在say函数中把say()函数的this用变量保存起来,即 varthat=this;  然后wk()函数使用that就能达到指向obj的目的了。另外的方式是通过apply或者call来改变。

(5) 那wk()在这里能不能写成window.wk()呢?这样是不行的,会报错,window.wk is not a function。为什么不行呢,this不是指向window吗,为什么widow对象里灭有wk()这个函数。。这个嘛,我也不知道,先留个坑,后面再来填 ×××

 

3.3  函数作为对象的属性去调用------例四

复制代码
var bj=10;
var obj={
    name:"八戒",
    age:"500",
    say:function(){
        var bj=40;
        console.log(this);//window
        console.log(this.bj);//10
        console.log(this.name);//这里没有输出内容
    }
}
var elseObj=obj.say;
elseObj();
复制代码

 (1) 执行了elseObj()函数之后,为什么say函数中的this却指向了window呢?首先要理解这句话:谁直接调用产生这个this指针的函数,this就指向谁。当obj.say赋值给elseObj的时候,elseObj只是一个函数,而并没有执行,因此this指针的指向并不明确,这个时候执行到 var elseObj=obj.say的 时候,整程序相当于:

复制代码
var bj=10;
var elseObj=function(){
    var bj=40;
    console.log(this);
    console.log(this.bj);
    console.log(this.name);
}
elseObj();
复制代码

     这就和 第2种 函数表达式的情况一样了。所以,当执行elseObj()的时候,this就指向window,this.obj为10,因为这时候elseObj()是通过 window 直接调用

(2) this.name为空是因为 window 对象中本身就有一个 name 值,并不是某处添加的,如果把name换成其它的比如age,得到的就是undefined了,因为全局并没有age属性。

 

3.4  函数作为对象的属性去调用------例五

复制代码
var bj=10;
var obj={
    name:"八戒",
    age:500,
    say:function(){
        return function(){
            console.log(this);//window
            console.log(this.bj);//10
            console.log(this.age);//undefined
        }
    }
}
obj.say()();
//    var elseObj=obj.say();
//    elseObj();
复制代码

(1) obj.say()()为什么会有两个括号?因为obj.say()执行之后返回的是一个函数,并没有执行,再加一个括号就是执行返回的那个匿名函数。

(2) 如果不习惯也可以使用上面注释的那种方式,是一样的效果。

(3) 执行了函数之后,为什么返回的函数中this是指向window的呢?那是因为执行obj.say()的时候,只是一个函数,相当于就是注释里的第一行代码,这时候返回的函数并未被执行。当再加一个括号的时候,就是执行了返回的那个函数,这个时候返回的函数就相当于是一个全局函数,是通过window直接调用,因此this就是指向的是window。

 

4.  工厂模式中this的指向------例一

复制代码
var bj=10;
function fun(a,b){
   console.log(this);//window对象 var bj=20; var sun=new Object(); sun.one=a; sun.two=b; sun.say=function(){ console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()} console.log(this.bj);//undefined console.log(this.one);//2 } return sun; } var wk=fun(2,3); wk.say();
复制代码

 (1) 话说为什么叫工厂模式,我搞不太清楚,不过这个不重要,重要的是通过这个模式,在每次调用函数的时候,虽然每次都返回的是sun这个对象,但是每个对象都是不相似的,即使内容一样,比如 var sf=fun(2,3); console.log(sf===wk);//false 。

(2) 那为什么say()函数执行之后,this是指向返回的那个对象呢?这个很明显嘛,say()是通过wk这个对象直接调用的,而wk是fun函数返回sun对象。所以这里的this就指向的是返回的对象。所以this.bj为undefined,因为返回的对象中没有bj属性。

(3) 我认为这种模式最重要的还是 renturn sun这个返回语句,这个是必不可少的。

(4) fun(a,b)这个函数中的this指向的是window,原因是执行 var wk=fun(2,3); 的时候,fun函数已经被执行了,并且直接调用它的就是window,所以这时的this是指向的window。

 

4.1  工厂模式中this的指向------例二

复制代码
var bj=10;
function fun(a,b){
   console.log(this);//window对象 var bj=20; var sun=new Object(); sun.one=a; sun.two=b; sun.say=function(){ console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()} return function(){ console.log(this);//是window对象 } } return sun; } var wk=fun(2,3); var ss=wk.say(); ss();
复制代码

 (1) 为什么say函数中return 的函数中this是指向的window对象呢?首先,执行到 var wk=fun(2,3); 的时候,wk是一个对象。继续执行下一句代码,ss这时候是一个函数,就是通过say函数返回之后赋值的。这时候返回的函数还未执行,this指向并不明确。当执行到最后一句代码,ss()函数执行了。这时候,ss函数就是一个全局函数,是通过window直接调用的。所以这时的this指向的是window。

(2) 如果say中返回的是一个对象,对象中又有个函数,像下面一样:

复制代码
sun.say=function(){
    console.log(this);//是sun对象,{one: 2, two: 3, say: ƒ()}
    return {
        wk:"1",
        say:function(){
            console.log(this);
        }
    }
}
复制代码

   这时候执行到ss.say()的时候,this指向的就是ss这个对象,即通过say函数返回的那个对象。原因还是一样,say函数是通过ss直接调用的,而ss对象是wk.say()返回的对象。

 

5.  构造函数中this的指向

复制代码
var bj=10;
function Add(){
    var bj=20;
    this.bj=30;
    this.say=function(){
        console.log(this);//Add {bj: 30, say: ƒ()}
        console.log(this.bj);//30
    }
     console.log(this) ;//Add {bj: 30, say: ƒ()}
}
var obj=new Add();
console.log(typeof obj);//object
obj.say();
复制代码

 (1) 要明白构造函数的this指向,我们需要明白调用构造函数经历的步骤:

  a。创建一个新对象。

  b。将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。

  c。执行构造函数中的代码(为这个新对象添加属性)。

  d。返回新对象。

  摘至js高程 6.2.2节。

(2) 构造函数与工厂模式相比:(原谅照搬js高程的话)。

  a。没有显示的创建对象。

  b。没有return语句。

  c。直接将属性和方法赋值给 this 对象。

  摘至js高程 6.2.2节。

(3)  首先,obj.say()执行之后,say函数中this的指向是obj对象,这个很明显,不再赘述。在不用new操作符的时候,Add()函数里的this指向的就是window;但是使用了new操作符之后,Add()函数中 console.log(this) 这个this为什么是obj对象,而不是window呢?

(4)  这个原因我认为在js权威指南4.6节对象创建表达式和8.2.3构造函数使用中,有所说明。使用new操作符的时候,js先创建一个新的空对象,然后,js传入指定的参数并将这个新对象当做this的值来调用一个指定的函数。这个函数可以使用this来初始化这个新创建对象的属性。所以当使用new操作符之后,函数中的this指向的是新创建的对象。所以构造函数中的this就是指向new出来的那个对象。

(5)  如果构造函数中有return语句,那么此时 var obj=new Add(); obj就是return出来的内容,但是Add函数中的this还是指向的创建的新对象Add;

 

6. 原型对象中this的指向

复制代码
var bj=10;
function Add(){
  console.log(this);//Add{}
}; Add.prototype.bj=10; Add.prototype.say=function(){ console.log(this);//Add{} return function(){ console.log(this);//window } } var obj=new Add;//没传参数可以省略括号 obj.say()();
复制代码

 (1)  obj.say()()执行的时候,this指向的是window,这个还是因为obj.say()执行时返回的是一个函数,然后再加一个括号,就执行返回的这个函数,此时这个函数属于全局函数,所以,this会指向window

(2)  Add()这个构造函数中的this指向的是Add{},原因和上面构造函数中this的指向一样。

(3)  Add.prototype.say=function(){ console.log(this) }  这里面的this 也是指向的是Add{},至于原因,我认为是因为say()这个函数是通过obj直接调用的,所以this指向的是obj,所以是Add{}。

 

总结:

  要想判断函数中this的指向,只要知道谁直接调用产生this指针的函数,this就指向谁了。只是要注意使用了new 操作符之后,构造函数内部的this指向的是新对象,通俗点讲就是new出来的新实例。

7、setTimeout和setinterval计时器中的this指向的是window对象,因为计时器执行时,脱离了原有的环境,在一个全新的环境中执行了,所以this指向window对象。

 8、箭头函数中的this指向问题

箭头函数中的this总是指向外层调用则!!!

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(() => {
            console.log(this.num);
        }, 1000)    //箭头函数中的this总是指向外层调用者,也就是Obj
    }
}
var obj = new Obj; 
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1
    function Aa(){
           this.fun=function(){
               setTimeout(function(){
                    console.log(this);
               },0);
           }
       }

       var obj = new Aa;
       obj.fun();//window

       function Bb(){
            this.fun=function(){
                setTimeout(()=>{
                    console.log(this);
                },0)
            }
       }
       var obj2 = new Bb;
       obj2.fun();//Bb

 

ES6中的箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj,因此利用箭头函数就可以轻松解决这个问题。

 

 

posted @ 2019-01-13 14:53  古墩古墩  Views(279)  Comments(0Edit  收藏  举报