使用 JavaScript 開發的時候,很多開發者多多少少會被 this 的指向搞蒙圈,但是實際上,關於 this 的指向,記住最核心的一句話:
哪個對象調用函數,函數里面的this指向哪個對象。
分幾種情況談論下:
一、普通函數調用
二、對象函數調用
三、構造函數調用
四、apply和call調用
五、箭頭函數調用
文章同步交流社區地址:http://www.mwcxs.top/page/427.html
一、普通函數調用
這個情況沒特殊意外,就是指向全局對象-window。
1、使用let
/*普通函數調用*/ let username = "程新松"; function fn(){ console.log(this.username); //undefined } fn();
使用node輸出的是:undefined

使用谷歌瀏覽器console輸出的是:undefined

2、使用var
var name = "程新松"; function fn(){ console.log(this.name); } fn();
使用node輸出的是:undefined
使用谷歌瀏覽器console輸出的是:程新松
3、使用window
window.username='程新松' function fn(){ console.log(this.username); } fn();
使用node輸出的是:報錯,window沒有定義

使用谷歌瀏覽器console輸出的是:

PS:為什么會出現這個錯誤呢,因為node里面沒有window 對象,瀏覽器中有window對象。
4、let和var區別:
(1)let 允許把變量的作用域限制在塊級域中;var 申明變量要么是全局的,要么是函數級的,而無法是塊級的。
let varClass = function(){ var name='程新松'; if(true){ var name='saucxs'; console.log(name); } console.log(name); } varClass(); // saucxs // saucxs

let letClass = function(){ let name='程新松'; if(true){ let name='saucxs'; console.log(name); } console.log(name); } letClass();

上面的結果說明了let只在{}內使用。
(2)先let后var
let subClass = function(){ let name='程新松'; if(true){ var name='saucxs'; console.log(name); } console.log(name); } subClass();

var 是函數級作用域,相當於一個作用域中有兩個n的變量了
var 作用於整個 subClass ,和let沖突了,let不能重復聲明,already been declared=已經被聲明
(3)先var后let
let subClass = function(){ var name='程新松'; if(true){ let name='saucxs'; console.log(name); } console.log(name); } subClass();

先申明var,再申明let,這個沒有問題。
二、對象函數調用
這個相信不難理解,就是哪個函數調用,this指向哪里。
/*對象函數調用*/ //window.name='程新松'; //var name='程新松'; let name='程新松'; let obj={ id:201102304, fn:function(){ console.log(this.name); //undefined console.log(this.id); //201102304 } } obj.fn();
,,
很明顯,第一次就是輸出 obj.name ,但是沒有這個name屬性,輸出的結果undefined。而第二次輸出obj.id,有這個id屬性,輸出 201102304,因為 this 指向 obj 。
有一種情況需要注意:
/*需要注意的情況*/ let obj1={ a:111 } let obj2={ a:222, fn:function(){ console.log(this.a); } } obj1.fn=obj2.fn; obj1.fn(); //111

這個也不難理解,雖然 obj1.fn 是從 obj2.fn 賦值而來,但是調用函數的是 obj1 ,所以 this 指向 obj1 。
三、構造函數調用
/*構造函數調用*/ let structureClass=function(){ this.name='程新松'; } let subClass1=new structureClass(); console.log(subClass1.name); let subClass=new structureClass(); subClass.name='成才'; console.log(subClass.name);

但是有一個坑,雖然一般不會出現,但是有必要提一下。
在構造函數里面返回一個對象,會直接返回這個對象,而不是執行構造函數后創建的對象
let structureClass=function(){ this.name='程新松'; return { username:'saucxs' } } let subClass1=new structureClass(); console.log(subClass1); console.log(subClass1.name);

四、apply和call調用
1、apply和call簡單來說就是會改變傳入函數的this。
/*apply和call調用*/ let obj1={ name:'程新松' }; let obj2={ name:'saucxs', fn:function(){ console.log(this.name); } } obj2.fn.call(obj1);

此時雖然是 obj2 調用方法,但是使用 了 call ,動態的把 this 指向到 obj1 。相當於這個 obj2.fn 這個執行環境是 obj1 。
call 和 apply 兩個主要用途:
1.改變 this 的指向(把 this 從 obj2 指向到 obj1 )
2.方法借用( obj1 沒有 fn ,只是借用 obj2 方法)
2、call與apply區別
call 和 apply 的作用,完全一樣,唯一的區別就是在參數上面。
call 接收的參數不固定,第一個參數是函數體內 this 的指向,第二個參數以下是依次傳入的參數。
apply接收兩個參數,第一個參數也是函數體內 this 的指向。第二個參數是一個集合對象(數組或者類數組)
/*apply和call區別*/ let fn=function(a,b,c){ console.log(a,b,c); } let arrArray=[1,2,3]; fn.call(window,arrArray); fn.apply(window,arrArray);

五、箭頭函數調用
首先不得不說,ES6 提供了箭頭函數,增加了我們的開發效率,但是在箭頭函數里面,沒有 this ,箭頭函數里面的 this 是繼承外面的環境。
/*箭頭函數調用*/ let obj={ name:'程新松', fn:function(){ setTimeout(function(){console.log(this.name)}) } } obj.fn();

不難發現,雖然 fn() 里面的 this 是指向 obj ,但是,傳給 setTimeout 的是普通函數, this 指向是 window , window 下面沒有 name ,所以這里輸出 underfind 。
換成箭頭函數
//換成箭頭函數 let obj={ name:"程新松", fn:function(){ setTimeout(()=>{console.log(this.name)}); } } obj.fn();

這次輸出 程新松 是因為,傳給 setTimeout 的是箭頭函數,然后箭頭函數里面沒有 this ,所以要向上層作用域查找,在這個例子上, setTimeout 的上層作用域是 fn 。而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭頭函數的 this ,指向 obj 。所以輸出 程新松。
