this深入理解

js中this指向有幾種情況

  1. 全局環(huán)境
  2. 函數(shù)調(diào)用
  3. 構(gòu)造調(diào)用
  4. apply、call、bind綁定
  5. 箭頭函數(shù)

全局環(huán)境

在瀏覽器中,無論是否在嚴格模式下,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)this 都指向全局對象。
在Nodejs環(huán)境,在全局執(zhí)行環(huán)境中,this指向module.exports。

函數(shù)調(diào)用

分析一般函數(shù)調(diào)用時this的指向,需要先引入引用類型的定義。

引用類型

A Reference is a resolved name or property binding. A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Symbol, a Number, or an Environment Record (8.1.1). A base value of undefined indicates that the Reference could not be resolved to a binding. The referenced name is a String or Symbol value.——ES2015 6.2.3 The Reference Specification Type

引用類型是一個ES規(guī)范定義的抽象類型,是為了解釋delete、typeof等概念使用,并非js語言中的類型。引用類型由3部分組成:

  1. base value,可能是undefined、object、boolean、string、symbol、number或一個環(huán)境記錄。是這個引用對應的屬性所處的上下文。
  2. referenced name,即引用對應的屬性的名。
  3. strict reference flag,標記是否處于嚴格模式下。
GetValue

ES定義的一個抽象方法,用于獲取引用類型真實的值。

在解釋相關(guān)概念后回到主題,普通的函數(shù)調(diào)用,this是根據(jù)函數(shù)調(diào)用操作符【(...)】左邊的值決定的,如果左邊的是引用類型的值,則this指向該引用類型的base。若非引用類型,則為undefined。

例如:

'use strict'
var x = 2;
var foo = {
    x : 1,
    bar: function () {
        console.log(this.x);
    }
};

foo.bar(); //1
(foo.bar = foo.bar)(); //Uncaught TypeError: Cannot read property 'x' of undefined  
//ps.如果沒有啟用嚴格模式,this為undefined時,會指向全局對象,則會輸出2

由于foo.bar = foo.bar采用了賦值操作,此時返回內(nèi)容為引用類型的真實值,所以返回的并非引用類型,此時this為undefined。

構(gòu)造調(diào)用

當一個函數(shù)被使用 new 操作符進行調(diào)用時稱為構(gòu)造調(diào)用時,在進行構(gòu)造調(diào)用時,函數(shù)內(nèi)部的this綁定到正在構(gòu)造的新對象上。new相關(guān)內(nèi)容會在后續(xù)文章中詳細寫。

apply、call、bind綁定

使用apply,call方法調(diào)用函數(shù)時,this將綁定到方法的第一個參數(shù)。例如:

var x = {
    a: 1
}

function test() {
    console.log(this.a);
}

test.apply(x); // 1 使用apply調(diào)用時,this.綁定到x上了
test.call(x); // 1
test(); //undefined

bind方法會創(chuàng)建一個新的函數(shù),新函數(shù)中的bind綁定到bind方法的第一個參數(shù)。bind創(chuàng)建的新函數(shù)不能再被bind進行綁定,也不會被apply、call修改。bind創(chuàng)建的新函數(shù)在進行構(gòu)造調(diào)用時會改變成指向新創(chuàng)建的對象。例如:

var x = {
    foo: 1
}

var y = {
    foo: 2
}

var z = {
    foo: 3
}

function test() {
    console.log(this.foo);
}

var a = test.bind(x);
a();//1 使用bind進行綁定

var b = a.bind(y);
b();//1 bind綁定創(chuàng)建的函數(shù)不能被二次綁定,this不再改變
a.call(y);//1 也不會因為apply、call調(diào)用改變this

new a(); // undefined 但是能被 new 操作符改變

var c = test.bind(z);
c();//3

箭頭函數(shù)

箭頭函數(shù)的this時根據(jù)詞法作用域的,this指向該函數(shù)創(chuàng)建時的環(huán)境。箭頭函數(shù)沒有進行this綁定,在箭頭函數(shù)中使用this,相當于尋找外部環(huán)境的this。此時this函數(shù)尋值跟普通變量尋值一樣,會在作用域鏈中逐層尋找。因為箭頭函數(shù)沒有自己的this,所以有以下幾個特點:

  1. 不能進行構(gòu)造調(diào)用,如:
function foo() {        
}

 var bar = ()=>{
 }
   
console.log(new foo()); // foo {}
console.log(new bar());// Uncaught TypeError: bar is not a constructor
  1. 不能通過apply、bind、call直接改變箭頭函數(shù)中this
  2. 改變所在作用域中的this的同時會改變箭頭函數(shù)中的this

例如:

var x = 10;
var test1 = {
     x : 20,
     y : function(){
         return ()=>{
             console.log(this.x);
         }
     },
     z : function(){
         return function(){
             console.log(this.x);
         }
     }
}

var test2 = { x : 30};
var test3 = { x : 40};

test2.y = test1.y();
test2.z = test1.z();

test2.y(); // 20 因為箭頭函數(shù)的this是在創(chuàng)建的時候確定的詞法作用域,實際上在test1.y()的時候就確定了,不會在代碼執(zhí)行時動態(tài)改變
test2.z();// 30 普通函數(shù)調(diào)用的this是動態(tài)作用域的,由于z的base是test2的環(huán)境記錄,所以取到的是test2的x

test2.y.call(test3);//20 箭頭函數(shù)沒有自己的this,所以不會被call改變
test2.z.call(test3);//40 普通函數(shù)會被call改變

test1.y.call(test2)();//30 箭頭函數(shù)在call的時候創(chuàng)建的,此時的上下文不是test1而是test2
test1.z.call(test2)();//10 普通函數(shù)的this是動態(tài)作用域的,此時為undefined,被自動轉(zhuǎn)化成了全局對象

箭頭函數(shù)常用于事件綁定、設(shè)置定時等回調(diào)函數(shù)。例如以下例子中,原意時希望1秒后再檢查t的值,但因為普通函數(shù)中的this是動態(tài)改變的,并不能得到想要的結(jié)果。

    function test(){
        this.x = 1;
        this.y = function(){
            console.log(this.x);
        }
    }

    var t = new test();
    setTimeout(t.y, 1000); //undefined

    t.x = 2;

此時可以使用箭頭函數(shù)解決,由于箭頭函數(shù)的this不會隨調(diào)用環(huán)境動態(tài)改變,所以可以得到想要的結(jié)果

    function test(){
        this.x = 1;
        this.y = ()=>{
            console.log(this.x);
        }
    }

    var t = new test();
    setTimeout(t.y, 1000);//2

    t.x = 2;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容