【前話】:轉發請事先聯繫,且不得應用於商業場景(筆者發現不打招呼被轉發,並用於賺錢,沒給我分潤?)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區別和使用
若是你喜歡這篇文章,請給我點個贊吧~~
只看不點贊,等於耍流氓~
●﹏●
●﹏●
●﹏●