javascript中this的指向問題
使用 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。所以輸出程新鬆。