js中的this指向问题

  前言

  在刚开始学习js的时候,我们肯定都会对this的指向问题感到很迷惑,特别是在一些较为复杂的代码里更是被this搞的晕头转向。其实,我们只需要记住一句话:this始终指向它的调用者

  好了,初步了解了this的指向问题,那么问题来了,我们又为什么要使用this呢?首先我们来看一段话:

  从前有座山,

  山里有座庙叫做神庙

  神庙里面有好多和尚,

  和尚们都很喜欢神庙

  于是和尚们都经常挑水到神庙

 大家可以看见这段话里面出现了很多次‘神庙’,不太符合我们平时说话的习惯,太过于啰嗦,实际上我们平时说话会保留第一个名词,接下来的话里如果提到这个名词都会用‘它’或者‘这’来代替这个   名词,比如这样:

  从前有座山,

  山里有座庙叫做神庙

  里面有好多和尚,

  和尚们都很喜欢

  于是和尚们都经常挑水到

  这样读起来是不是舒服多了,其实我们写代码时也是这样,就比如下面这段代码:

    var dog = {
        type: 'white',
        size: 'big',
        description: function (){
            console.log(
                    "The dog is" + dog.type + "and the size is" + dog.size
                    //"The dog is" + this.type + "and the size is" + this.size
            )
        }
    }
    dog.description();

 我们每次都用对象的名字来调用它的属性,如果对象名字很长或者要调用的属性很多,就会使代码量增加,代码看起来也不整洁

 

  this的四种绑定规则

1、默认绑定与隐式绑定

    function person() {
        console.log(this);
    }
    person();//window

  可以看到最后输出的是window对象,这就是默认绑定,之所以说是默认绑定,是因为person的调用不属于任何人,函数被调用的时候,this默认指向window全局对象

    function person() {
        console.log(this.name)
    }
    var p1 = {
        name: 'Andy',
        person: person
    }
    var p2 = {
        name: 'Mary',
        person: person
    }
    p1.person();//Andy
    p2.person();//Mary

  p1.person()和p1.person()这两种调用方式属于隐式绑定,person是作为p1和p2的方法被调用的,谁调用person,this就指向谁。

 

2、显示绑定

    function person() {
        console.log(this.name)
    }
    var p1 = {
        name: 'Andy',
    }
    var p2 = {
        name: 'Mary',
    }
    person.call(p1);//Andy   p1作为this
    person.call(p2);//Mary   p2作为this

如果person是通过call、apply、bind调用的,那么这就是显示绑定,想绑定哪个方法就绑定哪个方法。

 

3、关键字new绑定(构造函数绑定)

    function Person(name) {
        this.name = name;
        this.sayName = function (){
            console.log("我的名字是" + this.name)
        };
    }
    var name = "萌萌"
    var p = new Person("白白");
    p.sayName();//白白

new关键字放在一个函数调用的前面会js编译器会做四件事,new的详细介绍可以看我的另一篇,通过new绑定之后,用‘白白’这个变量名进行对象实例化,这个函数中的this就是这个新的对象p的实例化对象‘白白’,this就与‘白白’进行了绑定,即使出现了同名的变量name也是不会有影响的

 

4、箭头函数

 特别值得注意的是,箭头函数不绑定this,所以会无视以上所有的规则,但是它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值,和调用方式无关。接下来通过两个例子来说明这一点

 function Person(){
        this.age = 10;
        setTimeout(function () {
            console.log(this.age);     // 输出undefined
        }, 1000);
    }
    var p = new Person();
function Person(){
        this.age = 10;
        setTimeout(()=> {
            console.log(this.age);     // 输出10
        }, 1000);
    }
    var p = new Person();

   在上面没有使用箭头函数的例子当中,setTimeout内部的函数是被global调用的,而global没有age这个属性,因此输出undefined。

   第二个例子使用了箭头函数,this就会使用lexical scope(词法作用域)中的this,就是Person,因此输出10。

 

  绑定优先级

  如果多重绑定规格都适用,那么绑定规则的优先级顺序是这样的:

  1. 箭头函数
  2. 关键字new调
  3. 显式绑定
  4. 隐式绑定
  5. 默认绑定

  箭头函数优先级最高,会无视2-5绑定规则。而默认绑定优先级最低,只有其他绑定都不使用的时候,才会使用默认绑定。

 

  实例题

1、对象中的方法中的this

var o = {
    a: 10,
    b: {
        a: 12,
        fn: function(){
            console.log(this.a); //  12
            console.log(this); // 输出结果是 b 对象
        }
    }
}
//调用
o.b.fn(); 

 

2、改变调用方法,不直接调用:改用赋值后调用,此时this的指向为window,所以this.a的输出结果为 undefined,因为全局中没有全局变量a。

var o = {
    a: 10,
    b: {
        a: 12,
        fn: function(){
            console.log(this.a); //undefined 若在对象o外定义a,则输出的就是其在外定义的值(全局变量)
            console.log(this);   // window
        }
    }
}
var j = o.b.fn; //只是将b对象下的方法赋值给j,并没有调用
j(); //调用,此时绑定的对象是window,并非b对象直接调用  就是上面提到的默认绑定

 

3、在对象方法中调用

var point = {
        x : 0,
        y : 0,
        moveTo : function(x, y) {
            console.log(x);//1
            console.log(y);//1
            console.log(this.x); // 0
            console.log(this.y); // 0
        }
    };
    point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象

 

4、作为函数调用

function someFun(x) { 
    this.x = x; 
} 
someFun(5); //函数被调用时,this绑定的是全局对象 window,相当于直接声明了一个全局变量x,并赋值为5
console.log(x); //输出5   x 已经成为一个值为 5 的全局隐式变量

 

5、setInterval和setTimeout定时器中的this指向全局对象

var a = 10;
var oTimer1 = setInterval(function(){
    var a = 20;
    console.log(this.a); // 10
    clearInterval(oTimer1);
},100);

 

6、apply和call中的this指向参数中的对象

var a = 10;
var foo = {
    a: 20,
    fn: function(){
        console.log(this.a);
    }
};
var bar ={
    a: 30
}
foo.fn.apply();//10(若参数为空,默认指向全局对象)
foo.fn.apply(foo);//20
foo.fn.apply(bar);//30

 

7、作为构造函数

  function Point(x, y){
        console.log(this); // point对象  (函数在这里调用时输出window)
        this.x = x;
        this.y = y;
        this.moveTo = function(x,y){
            this.x = x;
            this.y = y;
            console.log(this.x);
            console.log(this.y);
        }
    }
var p1 = new Point(0,0); //注意这种形式方法的调用及apply、call的使用 var p2 = { x:0, y:0 }
p1.moveTo(
1,1); //1 1 p1.moveTo.apply(p2,[10,10]);//10 10 console.log(x);// x is not defined console.log(y);//

 

 

好啦,关于this的学习就到这里了,文中部分内容转载自:https://zhuanlan.zhihu.com/p/82504422

https://www.jianshu.com/p/5f8440535a2a

 

posted @ 2021-10-15 19:40  打遍天下吴敌手  阅读(207)  评论(0编辑  收藏  举报