摘要:綁定使用來調用函數,或者說發生構造函數調用時,會自動執行下面的操作。
1 this的概念
this 在任何情況下都不指向函數的詞法作用域。作用域“對象”無法通過 JavaScript代碼訪問,它存在于 JavaScript 引擎內部。
this 是在運行時進行綁定的,并不是在編寫時綁定,它的上下文取決于函數調用時的各種條件。this 的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式。
當一個函數被調用時,會創建一個活動記錄(有時候也稱為執行上下文)。這個記錄會包含函數在哪里被調用(調用棧)、函數的調用方法、傳入的參數等信息。this 就是記錄的其中一個屬性,會在函數執行的過程中用到。
2 this的綁定函數的執行過程中調用位置如何決定 this 的綁定對象,綁定規則:
1 默認綁定
function foo(){ console.log(a); } var a=2; foo();
foo() 是直接使用不帶任何修飾的函數引用進行調用的,因此只能使用默認綁定,無法應用其他規則。如果使用嚴格模式(strict mode),那么全局對象將無法使用默認綁定,因此 this 會綁定
到 undefined。
2 隱式綁定
調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含。當 foo() 被調用時,它的落腳點確實指向 obj 對象。當函數引用有上下文對象時,隱式綁定規則會把函數調用中的 this 綁定到這個上下文對象。因為調用 foo() 時 this 被綁定到 obj,因此 this.a 和 obj.a 是一樣的。對象屬性引用鏈中只有最頂層或者說最后一層會影響調用位置。
function fooo(){ conosle.log(this.a); } var obj={ a:2, foo:foo }; var obj2={ a:22, obj: obj } obj.foo(); // 2 obj2.obj.foo(); // 2
隱式丟失
雖然 bar 是 obj.foo 的一個引用,但是實際上,它引用的是 foo 函數本身,因此此時的bar() 其實是一個不帶任何修飾的函數調用,因此應用了默認綁定。
當函數被作為參數傳遞時(如:回調函數),會丟失this,因為參數傳遞其實就是一種隱式賦值
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函數別名! var a = "oops, global"; // a 是全局對象的屬性 bar(); // "oops, global"
3 顯式綁定
JavaScript 提供的絕大多數函數以及你自己創建的所有函數都可以使用 call(..) 和 apply(..) 方法。這兩個方法是如何工作的呢?它們的第一個參數是一個對象,它們會把這個對象綁定到this,接著在調用函數時指定這個 this。因為你可以直接指定 this 的綁定對象,因此我們稱之為顯式綁定。
function foo(){ console.log(this.a); } var obj={a:2}; foo.call(obj); // 2
如果你傳入了一個原始值(字符串類型、布爾類型或者數字類型)來當作 this 的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String(..)、new Boolean(..)或者new Number(..))。這通常被稱為“裝箱”。
4 new 綁定
使用 new 來調用函數,或者說發生構造函數調用時,會自動執行下面的操作。
創建(或者說構造)一個全新的對象。
這個新對象會被執行[[原型]]連接。
這個新對象會綁定到函數調用的this。
如果函數沒有返回其他對象,那么new表達式中的函數調用會自動返回這個新對象。
function foo(a) { this.a = a; } var bar = new foo(2); console.log( bar.a ); // 2
使用 new 來調用 foo(..) 時,我們會構造一個新對象并把它綁定到 foo(..) 調用中的 this上。new 是最后一種可以影響函數調用時 this 綁定行為的方法,我們稱之為 new 綁定。
3 判斷this綁定?
注意:如果你把 null 或者 undefined 作為 this 的綁定對象傳入 call、apply 或者 bind,這些值在調用時會被忽略,實際應用的是默認綁定規則。
那么什么情況下你會傳入 null 呢?一種非常常見的做法是使用 apply(..) 來“展開”一個數組,并當作參數傳入一個函數。類似地,bind(..) 可以對參數進行柯里化(預先設置一些參數),這種方法有時非常有用:
function foo(a,b) { console.log( "a:" + a + ", b:" + b ); } // 把數組“展開”成參數 foo.apply( null, [2, 3] ); // a:2, b:3 // 使用 bind(..) 進行柯里化 var bar = foo.bind( null, 2 ); bar( 3 ); // a:2, b:3
軟綁定會對指定的函數進行封裝,首先檢查調用時的 this,如果 this 綁定到全局對象或者 undefined,那就把指定的默認對象 obj 綁定到 this,否則不會修改 this。
if (!Function.prototype.softBind) { Function.prototype.softBind = function(obj) { var fn = this; // 捕獲所有 curried 參數 var curried = [].slice.call( arguments, 1 ); var bound = function() { return fn.apply( (!this || this === (window || global)) ? obj : this curried.concat.apply( curried, arguments ) ); }; bound.prototype = Object.create( fn.prototype ); return bound; }; }4 箭頭函數
箭頭函數并不是使用 function 關鍵字定義的,而是使用被稱為“胖箭頭”的操作符 => 定義的。箭頭函數不使用 this 的四種標準規則,而是根據外層(函數或者全局)作用域來決定 this。
function foo() { // 返回一個箭頭函數 return (a) => { //this 繼承自 foo() console.log( this.a ); }; } var obj1 = { a:2}; var obj2 = { a:3}; var bar = foo.call( obj1 ); bar.call( obj2 ); // 2, 不是 3 !
foo() 內部創建的箭頭函數會捕獲調用時 foo() 的 this。由于 foo() 的 this 綁定到 obj1,bar(引用箭頭函數)的 this 也會綁定到 obj1,箭頭函數的綁定無法被修改。(new 也不行!)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104376.html
下一篇:《你不知道的javascript》筆記_對象&原型 寫在前面 上一篇博客我們知道詞法作用域是由變量書寫的位置決定的,那this又是在哪里確定的呢?如何能夠精準的判斷this的指向?這篇博客會逐條闡述 書中有這樣幾句話: this是在運行時進行綁定的,并不是在編寫時綁定,它的上下文取決于函數調用時的各種條件this的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式當一個函數被調用時...
摘要:本書屬于基礎類書籍,會有比較多的基礎知識,所以這里僅記錄平常不怎么容易注意到的知識點,不會全記,供大家和自己翻閱不錯,下冊的知識點就這么少,非常不推介看下冊上中下三本的讀書筆記你不知道的上讀書筆記你不知道的中讀書筆記你不知道的下讀書筆記第三 本書屬于基礎類書籍,會有比較多的基礎知識,所以這里僅記錄平常不怎么容易注意到的知識點,不會全記,供大家和自己翻閱; 不錯,下冊的知識點就這么少,非...
摘要:注此讀書筆記只記錄本人原先不太理解的內容經過閱讀你不知道的后的理解。作用域及閉包基礎,代碼運行的幕后工作者引擎及編譯器。 注:此讀書筆記只記錄本人原先不太理解的內容經過閱讀《你不知道的JS》后的理解。 作用域及閉包基礎,JS代碼運行的幕后工作者:引擎及編譯器。引擎負責JS程序的編譯及執行,編譯器負責詞法分析和代碼生成。那么作用域就像一個容器,引擎及編譯器都從這里提取東西。 ...
摘要:有種內置類型,分別是除對象之外,其他統稱為基本類型。另一個需要注意的是數組確切地說,數組也是的一個子類型我們可以通過下面的方法檢查變量是不是數組處理未聲明的變量時,會返回這是因為有一個特殊的安全防范機制。 js有7種內置類型,分別是undefined null boolean string number symbol object除對象之 Object 外,其他統稱為基本類型。符號 ...
摘要:閉包在循環中的應用延遲函數的回調會在循環結束時才執行事實上,當定時器運行時即使沒給迭代中執行的是多有的回調函數依然是在循環結束后才會被執行,因此會每次輸出一個出來。 閉包在循環中的應用 延遲函數的回調會在循環結束時才執行;事實上,當定時器運行時即使沒給迭代中執行的是 setTime(..., 0),多有的回調函數依然是在循環結束后才會被執行,因此會每次輸出一個6出來。 for(var...
閱讀 1310·2021-11-22 14:44
閱讀 2445·2021-09-30 09:47
閱讀 1221·2021-09-09 11:56
閱讀 2077·2021-09-08 09:45
閱讀 3953·2021-08-31 09:40
閱讀 1250·2019-08-30 15:52
閱讀 2044·2019-08-30 14:09
閱讀 1578·2019-08-26 17:04