1. 程式人生 > 實用技巧 >javascript中this的指向問題

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

callapply兩個主要用途:

1.改變this的指向(把thisobj2指向到obj1

2.方法借用(obj1沒有fn,只是借用obj2方法)

2、call與apply區別

callapply的作用,完全一樣,唯一的區別就是在引數上面。

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指向是windowwindow下面沒有name,所以這裡輸出underfind

換成箭頭函式

//換成箭頭函式
let obj={
    name:"程新鬆",
    fn:function(){
        setTimeout(()=>{console.log(this.name)});
    }
}
obj.fn();

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