一文摸清javascript 函数作用域中的关键字this指向

2018-02-0206:49:11WEB前端开发Comments1,908 views字数 4041阅读模式

初学者来说,关键字this总是让人捉摸不透,甚是复杂,但是其实,只要你摸透了其中关键所在,便也无甚烦恼了;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

其实this在函数中指向的是什么,都是围绕两个原则,就是谁引用他他就指向谁与就近原则;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

一般this的指向性问题分为下面几类,我们来看具体实例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

一、普通函数中的this关键字

//示例一
function f1(){
    this.a = 5;
}
f1();
console.log(window.a) // 5;

//示例二
var b = 10;
function f2(){
    this.b = 20;
}
f2();
console.log(window.b) //20;

上面两个例子都是普通函数,他们指向的都是window,所以我们的结论是:
普通函数中this的指向都是window文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

可能你会问,上面不是说,this指向的都是调用他的那个对象吗?那函数自执行,this的指向不应该是函数本身吗?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

其实啊,函数自己调用自己的时候,就相当于是window在调用他,像示例一种,f1()相当于是window.f1();文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

既然是window调用他,那么this的指向就是window了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

二、对象中的this关键字

同样我们先看下面例子:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

//示例一
var obj1 = {
    a : 5,
    f1 : function(){
        console.log(this.a) // 5
        console.log(this === obj1) //true
    }
}
obj1.f1();

从给出的例子我们可以看出来,当函数作为对象的属性来调用时,this的指向便指向了对象自身,这就是说既然你函数都是我对象的一个属性了,那么你里面的this当然指向我,这种指向是一种隐式绑定this的现象,但是如果我们解除了这种隐式绑定呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

接下来我们将例子变一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

//变异示例一
var obj2 = {
    a : 5,
    f2 : function(){
        console.log(this.a); //undefined
        console.log(this === obj2) //false
    }
}
var init = obj2.f2 //重点看这里
init();

还是那句话,谁调用指向谁,从变异示例一可以看出,obj.f2这里并没有调用函数f2,他只是将函数赋值给了变量init而已,所以这里调用函数f2的不再是对象obj2了,因为已经将f2的隐式绑定给解除了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

所以必然this指向的不在是obj2,那么obj2中的属性自然是调用不到了,this也不可能等于obj2了,那么这里的this究竟指向了谁呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

一起看init()这里,在这里调用了函数,变量的是全局变量,他的顶级对象在这里是window,所以init() === window.init()是一样的,所以最后this指向的是window;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

这里我们要注意,一定要看清楚函数是在哪里调用的,这样我们才能去判断this指向的是什么文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

再将上面的示例变一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

var obj3 = {
    a : 5,
    obj4 : {
        a : 10,
        f3 : function(){
            console.log(this.a) //10
        }
    }
}
obj3.obj4.f3();

这里输出的为什么是10呢?按最终调用的应该是对象obj3,那么他下面的a不应该是5吗?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

还记得我们上面说过一个词叫就近原则吗?虽然最终调用的是obj3,但是在遵循就近原则的情况下,调用他的是obj4,所以this指向的是obj4,那么必然打印出来的就是10了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

最后我们附加一个示例,这也是之前有个人有些疑问的地方,我觉得值得说一下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

var obj5 = {
    a : 5,
    f5 : function(){
        console.log(this.a) // 5
        console.log(this === obj5) //true
    }
}

var init = obj5;
init.f5();

在这里直接将对象obj5赋值给变量init,然后由变量去调用f5,实则和obj5去调用f5是一样的,this指向的还是对象obj5,所以在这里一定要注意,千万不要认为是window调用函数f5文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

三、call中的this关键字

call() 、bind()、apply()这类方法绑定this关键字又称为显示绑定,明面上我们完全可以看到他指向了谁文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

function f7(){
    this.x = function(){
        console.log(this.a) // 20
    }
}
var obj5 = {
    a : 20
}
f7.call(obj5);
obj5.x();

通过call()方法,函数f7被封装为对象obj5的一个属性,this的值指向了调用函数的对象obj5了,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

通过对call()方法的使用,我们知道,如果第一个参数为null,或者不写,那么其间的对象就是window,我们看下面示例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

function f8(){
    this.x = function(){
        console.log(this.a) // 15
    }
}
var obj6 = {
    a : 20
}
var a = 15;
f8.call(null);
x();
//我们可以看到,这段函数里面并没有定义过函数x,但是在这里可以直接调用
//这说明,我们的this指向了window,x() === window.x();

四、构造函数中的this关键字

先看示例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

function F1(){
    this.a = 10;
    this.b = 20;
    this.f6 = function(){
        this.c = this.a+this.b;
        console.log(this.c); //30
        console.log(this === F1) //false
    }
}

var oInit = new F1();
oInit.f6();

这里就很奇怪了,明明所有症状都表现为this指向的就是构造函数F1,但是为什么this没有指向F1呢,在这里,我们需要了解,在new F1()的这个过程中,new到底做了什么?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

看下面代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

//new 过程如下
var obj = {};
//new 先创建了一个新对象(第一步)
//实则这里已将this指向了这个新对象
obj.__proto__ = F1.prototype 
//对象obj的隐式原型指向F1的原型对象(第二步)
F1.call(obj);//继承了F1中所有属性与方法
return obj //最后将obj return出去    (第四步)
//而Oinit = obj
//所以最终Oinit继承了F1的一切属性,而其中this便指向了函数F1的实例化对象Oinit。

我看到很多人说,构造函数中的this指向的是构造函数本身,这种说法是不正确的,
他就算瞎指也不会指向函数本身,谁去调用他是吧,他指向的new的实例,所以一定要注意这里文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

在构造函数中使用return有个特殊情况,我们来看看文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

//示例一
function f9(){
    this.a = 10;
    var obj10 = {};
    obj10.a = 20;
    return this.a
}
var init2 = new f9();
init2.a;


//示例二
function f9(){
    this.a = 10;
    var obj10 = {};
    obj10.a = 20;
    return obj10;
}
var init2 = new f9();
init2.a;

这两个示例有点特殊,特殊在于return出来的值,这里先不说this指向的问题,说说return返回的值的问题,在函数中如果返回的是一个对象(包括函数,数组,对象),即返回出来的是对象本身,就会覆盖原来的this对象,如果返回的是值类型, 则是返回this对象;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

所以从表面上看是觉得构造函数的实例化对象在调用参数,其实不然,内部正真调用的是返回的对象,那么自然this值指向了返回的对象文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

五、事件中的this关键字

1、事件中直接使用this文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

<div class="clickMe">click me</div>
<script>
    var clickDom = document.querySelector('.clickMe');

    clickDom.onclick = function(){
        console.log(this.className) //clickMe
    }
</script>

这里this指向了操作事件的元素,所以如果直接在事件中使用this,那么this则指向操作事件的元素;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

2、事件中的函数使用this;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

<div class="clickMe">click me</div>
<script>
    var clickDom = document.querySelector('.clickMe');

    clickDom.onclick = function(){
        function test(){
            console.log(this.className) //undefined
            console.log(this) //[object Window]
        }
        test();
    }
</script>

这里指向了window,我们是不是可以得出结论,如果在事件中的函数去调用this,那么就看该函数是谁调用的呢?先不急,我们再看下面一个示例:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

<div class="clickMe">click me</div>
<script>
    var clickDom = document.querySelector('.clickMe');

    clickDom.onclick = function(){
        var obj7 = {
            a : 10,
            f7 : function(){
                console.log(this.a); //10
            } 
        }
        obj7.f7();
    }
</script>

这里我们完全可以确定,就算事件中,this的指向还是遵循一个原则,谁调用指向谁 + 就近原则;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

事件后面跟的是一个匿名函数,而调用这个匿名函数的则是事件中的元素,所以自然是指向了调用事件的元素了,实则上面的示例无不在佐证这一原则文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

好了,关于this的指向性问题,就讲到这里了,还是那句话,谁调用指向谁 + 就近原则 ,万般皆虚幻,透过现象看本质,才能正真知道this指向的是谁文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

作者:深谷逸风
链接:https://juejin.im/post/5a0ce9ae51882561a20a0573
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/379.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/379.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定