【前话】:转发请事先联系,且不得应用于商业场景(笔者发现不打招呼被转发,并用于赚钱,没给我分润?)javascript
相信不少前端人对“this”的指向是很懵逼的,由于this的指向老是变幻莫测,在不一样的调用环境中,它的指向老是各不相同。前端
在面试中,this也是常常考的必考题之一,不少前端老鸟常常会在this这里掉坑。java
接下来,看笔者来一层一层的揭开this指向的面纱。node
1.事件调用环境中的this指向
<div class="box1"></div> <div class="box2"></div> <script> const box1 = document.querySelector('.box1') const box2 = document.querySelector('.box2') function event() { console.log(this) } box1.onclick = event ------> <div class="box1"></div> box2.onclick = event ------> <div class="box2"></div> event() ------> window对象 </script>
从上述实例中能够看出:面试
box1点击后,event方法中this指向div.class.box1数组
box2点击后,event方法中this指向div.class.box2浏览器
event()直接调用,this指向window对象,严格模式下指向的是undefinedapp
总结:1.当事件被动调用时,谁去触发,this就指向谁; 2.当时间主动执行时(事件后面加()), this指向的就是window,严格模式下指向的是undefined函数
2.全局环境下的this指向
前端全局环境分两部分,浏览器环境,node环境测试
浏览器全局环境
<script> var aaaa = 100000 function aaatestEvent() { console.log(this) } // console.log(this) ------> window对象 // aaatestEvent() === window.aaatestEvent() ------> window对象 // aaa = window.aaa </script>
我相信有不少小伙伴应该试过在script书写的时候直接打印出this的指向,看看this指向的是个什么东东?没错,指向的是window全局对象;咱们常常定义的常量和变量也存放在window对象中
node环境
console.log(this) -----> {} console.log(this === module.exports) -----> true
小伙伴从上述实例中能够看出,在node环境中,this指向的是暴露出来的对象
总结:全局环境中,浏览器环境指向的是window对象,node环境指向的是暴露出来的object对象
3.函数内部环境中的this指向
在实际开发中,调用函数方法时,为了节约对内存的消耗,函数方法常常被存放于一个object对象中去,有利于方法的管理性和可读性
实例1:
<script> var obj = { num: 100, fn: function () { console.log(this) } } // 调用 obj.fn() ----> this指向obj </script>
obj.fn()运行后,this指向的obj,能够发现,函数方法存在对象中的时候,函数内部的this指向的调用它的对象
实例2:
<script> var obj = { num: 100, fn: function () { console.log(this) } } // 调用 obj.fn() ----> this指向obj window.obj.fn() ---> this指向obj </script>
var obj1 = { num: 100, aaa: { fn: function () { console.log(this) } } } // 调用 window.obj1.aaa.fn() ---> this指向aaa
经过实例1中的测试,咱们稍微总结了下this指向的是调用它的对象;
咱们在上面浏览器全局环境中讲过,定义常量和变量会被存放在window对象中,经过window对象输出,咱们也能发现obj在window中,能够尝试去执行
window.obj.fn(),咱们发现this仍是指向obj啊?笔者刚刚不是说this指向的是调用它的对象吗?如今是window在调用obj的fn方法啊?怎么回事?
window.obj1.aaa.fn(),咱们发现this指向了aaa啊?
解析:
实例1:obj.fn() fn被obj调用,this指向obj
实例2:window.obj.fn() fn被window调用,this指向obj
window.obj1.aaa.fn() fn被window调用,this指向aaa
此时,咱们总结:
实例1:【this指向调用它的对象】
实例2:【函数被多层对象包含,函数被最外层对象调用时,this指向的是它的上一级的对象】
有不少面试过程当中,常常在这个地方会被问到,注意别掉坑哦?
有些考官特别可恶,喜欢在这个地方设置障碍故意绕懵面试者,请看下面的测试实例
<script> var obj = { num: 100, aaa: { fn: function () { console.log(this) } } }; var abc = obj.aaa.fn; window.obj.aaa.fn() ------> 指向aaa abc() ------>指向window abc()等同于window.abc(),此处abc省略了window </script>
解析上面实例
window.obj.aaa.fn() 就是上面总结的实例2,this指向的是它的上一级的对象
此时假如window.obj.aaa.fn被定义为到了一个abc上,调用abc()就至关于调用window.abc(), 实例1的总结是这样的:this指向调用它的对象,this的输出执行是abc方法被执行了,谁调用了?没错window对象调用了,因此this指向的就是window对象,此处很是的绕,请小伙伴们仔细思考哦!
4.构造函数环境中的this指向
哈哈哈,凡事都是有例外的,当咱们在写构造函数或者在写面向对象的逻辑中的时候,this的指向状况又不同了,请看实例:
不含返回值的构造函数
<script> function fn() { this.num = 10 console.log(this) } var obj = new fn(); console.log(obj) /* * new在此时的做用 * * 1. 调用fn这个函数方法 * 2. 自动建立一个object对象 * 3. 把建立出来的对象与this进行绑定 * 4。若是构造函数没有返回值,隐式返回this对象 * */ </script>
上面的例子能够看出
this返回的东西和fn new出来的实例返回的东西是同样的,这个就是new的魅力
哈哈哈,扯远了,主要是照顾一下刚入前端坑的小伙伴们,解释下new所作的事情
1. 调用fn这个函数方法
2. 自动建立一个object对象
3. 把建立出来的对象与this进行绑定
4. 若是构造函数没有返回值,隐式返回this对象
总结:构造函数中,this指向的是新建立出来的而且若是又属性值,进行绑定属性的新建对象
ok, ok,anyway!咱们再来个复杂的,同时也是更有意思的实例
<script> function fn() { this.num = 10 console.log(this) } // 此处的num与fn中的num不是同一个东西哦 fn.num = 20; // 每一个方法都有本身的原型链,在原型链上定义一个num fn.prototype.num = 30; // 在原型链上定义一个method的方法 fn.prototype.method = function () { console.log(this); } var prototype = fn.prototype; 就至关于var prototype = {} var method = prototype.method // 直接new fn new fn() new fn().method() prototype.method() method() </script>
实例解析:
1.new fn() 刚刚说了,this指向的是新建立出来的而且若是又属性值,进行绑定属性的新建对象 ,因此,此时this输出的值是{num: 10}
2.new fn().method(), new的做用说了,先调用方法,而后建立一个对象,而后进行绑定this,最后输出;
new fn().method()就至关于{}.method(), 空对象{}绑定this下面的num = 10,因此此时this输出的值是{num: 10}
3.函数方法都有原型链,原型链就是一个实例化了空对象{}进行扩展,并绑定原型链上的属性(能够理解为new了一个原型链)
prototype.method()执行中,var prototype = fn.prototype 就至关于 var prototype = {},原型链上含有不少私有方法和定义属性等等,
好比咱们定义的fn.prototype.num = 30; fn.prototype.method = function () { console.log(this); }
因此此时this输出的值是{num: 30,......}
4.method()就至关于window.method(),很显然,全局环境中的this指向的是window, 全局window对象没有定义num,因此若是输出this.num就是undefined
这个实例很大程度依赖小伙伴们对js基础知识掌握的考验
总结:在没有返回值的构造函数中,this指向的是new实例化建立出来的对象,对象中与函数this进行绑定(原型链相同原理,参考解析3)
有return返回值的构造函数
看实例
<script> function fn() { this.num = 10; return '' /* * '' 空字符串 ---> 10 * [] 数组 --->undefined * {} 对象 ---> 10 * 123 数字 ---> 10 * function(){} 方法 ---> undefined * null ------> 10 * */ } var obj = new fn() console.log(obj.num); </script>
从上述实例中,咱们能够
总结:构造函数有返回值,当返回值是对象(type of进行类型查看)的时候,this指向的是实例化建立出来的对象(obj)
当返回值不是对象的时候,保持本来规则不变,
此处null是一个特例,返回null时,this指向的是实例化建立出来的对象(obj)
5.箭头函数环境中的this指向
常常有前端刚入门的小伙伴,this指向发生偏移的时候,有些同伴就说,赶忙用ES6的箭头函数啊?这样this的指向就不会变了,至于为何不会变,本身都说不出来的状况
接下来,让咱们剖析一下箭头箭头函数
<script> var box1 = document.querySelector('.box1'); var box2 = document.querySelector('.box2'); box1.onclick = move1; box2.onclick = move2; function move1() { setTimeout(function () { console.log(this); }, 1000) } function move2() { setTimeout(() => { console.log(this); }, 1000) } </script>
解析:为什么在setTimeout中使用function(){}和箭头函数() => {}this的指向不同呢?
延迟调用的函数在setTimeout中是以入参的形式调用的
1. 其实若是传入的入参提到外面进行定义,不就是以下面同样嘛,这是不难理解,fn()就是至关于window.fn(),因此this指向window对象
function fn() { console.log(this) } function move1() { setTimeout(fn, 1000) }
2.入参以箭头函数形式进行程序执行,this输出为调用的节点了,是否是以下面同样了
<script> var obj = { fn: () => { console.log(this) } } // obj是不能造成独立做用域的 obj.fn() ------> this指向的是window对象 </script>
总结:箭头函数中的this指向官方文档指的是上下文环境中的this;
咱们理解为:箭头函数自己是没有this和argument的,在箭头函数中调用this实际上就是在调用定义在上一层做用域的this指向;这里强调一下,指的是上一层做用域,由于对象是不能造成独立的做用域的。
this的指向经常使用的几种状况都在这里了,小伙伴们请认真阅读,仔细思考才能不被绕晕哦,待看后期广大借阅者理解程度,若是理解的不是很透彻,笔者能够考虑录个视频也无不可哦…………^ - ^
6.修改this指向
关于修改this的指向的方法,能够参考一下个人另外一篇博文:Javascript千面之call、apply、bind区别和使用
若是你喜欢这篇文章,请给我点个赞吧~~
只看不点赞,等于耍流氓~
●﹏●
●﹏●
●﹏●