《你不知道的javascript》筆記_this

下一篇:《你不知道的javascript》筆記_對象&原型javascript

寫在前面

上一篇博客咱們知道詞法做用域是由變量書寫的位置決定的,那this又是在哪裏肯定的呢?如何可以精準的判斷this的指向?這篇博客會逐條闡述java

書中有這樣幾句話:segmentfault

this是在運行時進行綁定的,並非在編寫時綁定,它的上下文取決於函數調用時的各類條件
this的綁定和函數聲明的位置沒有任何關係,只取決於函數的調用方式

當一個函數被調用時,會建立一個活動記錄(有時候也稱爲執行上下文)。這個記錄會包含函數在哪裏被調用(調用棧)、函數的調用方法、傳入的參數等信息。this 就是記錄的其中一個屬性,會在函數執行的過程當中用到。閉包

關於執行上下文,能夠參考《javascript高級程序設計》筆記:內存與執行環境app

1、 綁定規則

1.1 默認綁定

最經常使用的函數調用類型——獨立函數調用,使用的即爲默認綁定規則,在非 strict mode下,this指向全局對象
function foo1() {
    console.log(this.a);
}
var a = 10;
foo1(); // 10

// 即便函數嵌套比較深
function foo2() {
    foo1();
}
function foo3() {
    foo2();
}
foo3();

固然,咱們實際使用中,難以判別的並非直接型的默認綁定模式,而是隱式綁定丟失型的默認綁定(下面會着重說明)函數

1.2 隱式綁定【重點】

調用的位置是否有上下文對象,或者說被某個對象擁有或包含
// 基本形式
function foo() {
    console.log(this.a);
}
var obj = { a: 10, foo };
obj.foo(); // 10

隱式綁定中的幾個雷區:oop

1. 多個對象嵌套引用時,只有最後一層在調用位置中起做用this

function foo() {
    console.log(this.a);
}
var obj2 = { a: 42, foo };
var obj1 = { a: 10, obj2 };
obj1.obj2.foo(); // 42

2.【隱式丟失】當調用函數被從新賦值爲新變量,調用新變量時this指向會有不一樣prototype

// 共用部分
function foo(){
    console.log(this.a);
}
var obj = { a: 10, foo };
var a = 'opps, global';

// 直接賦值
var bar = obj.foo;
bar(); // 'oops, global'

// 回調間接賦值1
function doFoo(fn) {
    fn();
}
doFoo(obj.foo); // 'oops, global' 至關於間接賦值

// 回調間接賦值2
setTimeout(obj.foo, 100); // 'oops, global' 內置的setTimeout也至關於間接賦值

經典綜合案例:設計

var length = 10;
function fn(){
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function (fn) {
        fn();
        arguments[0]();
    }
};

obj.method(fn, 123);

分析:fn()爲函數fn的引用,默認綁定,指向全局;arguments[0]();至關於下面的引用,數據隱式綁定,綁定對象爲arguments,其屬性length值爲參數數量2

arguments: {
    '0': function fn(){
        console.log(this.length);
    }
}

答案:10 2

1.3 顯式綁定

call()/ apply()/ bind()可以顯式修改 this指向

經過上述方法調用的方式爲顯示綁定,它們第一個參數是一個對象,在調用函數時,綁定在this中。

關於三者的基本用法和說明在以前博客《javascript高級程序設計》函數調用模式 & this深度理解中已做說明,在此不作嘮述

兩點注意

1. 經過顯式綁定的不能再修改它的this指向

function foo() {
    console.log(this.a);
}
var obj = { a: 2 };
var bar = function() {
    foo.call(obj);
}
bar(); // 2
setTimeout(bar, 200); // 2

bar.call(window); // 2

2. 將null/undefined做爲第一個參數時,調用會忽略這些值,採用默認綁定規則

function foo() {
    console.log(this.a);
}
var a = 2;
foo.call(null); // 2

1.4 new綁定

使用關鍵字 new執行函數,當函數無返回值或返回值非對象時, this指向爲實例對象

new關鍵字執行函數流程:

  1. 建立一個全新的對象
  2. 這個新對象會被執行[[prototype]]鏈接
  3. 這個新對象會綁定到函數調用的this
  4. 若是函數沒有返回其餘對象,所執行函數會自動返回這個新的對象

須知:構造函數與普通函數無異,做爲區分,咱們通常講經過new調用的函數稱爲構造函數,並大寫第一個單詞。全部函數都可由關鍵字new調用

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2

2、優先級&判斷規則

2.1 優先級

new綁定 --> 顯式綁定 --> 隱式綁定 --> 默認綁定

2.2. 判斷規則【重點】

  1. new綁定】函數是否在new中調用?若是是,this綁定的是新建立的對象
  2. 顯式綁定】函數是否在call/aplly/bind中調用?若是是,this綁定的是指定對象
  3. 隱式綁定】函數是否在某個上下文中調用?若是是,this綁定到那個上下文對象
  4. 默認綁定】若是都不是,this綁定嚴格模式下爲undefined,非嚴格模式下爲全局對象

3、箭頭函數中的this

ES6中箭頭函數不使用上面 this的四種標準規格,而是根據外層(函數或者全局)做用域來決定 this指向

下面是一個普通函數和箭頭函數的對比:

function foo1() {
    setTimeout(() => {
        console.log(this.a)
    }, 100)
}
function foo2() {
    setTimeout(function() {
        console.log(this.a)
    }, 100)
}

var a = 10;
var obj = { a: 2 };

foo1.call(obj); // 2 箭頭函數this指向外層(obj)
foo2.call(obj); // 10 隱式丟失,默認綁定

【利用閉包】理解箭頭函數中的this:

// 上例中的箭頭函數至關於
function foo1() {
    var self = this;
    setTimeout(function() {
        console.log(self.a)
    }, 100)
}

上一篇:《你不知道的javascript》筆記_做用域與閉包
下一篇:《你不知道的javascript》筆記_對象&原型

相關文章
相關標籤/搜索