摘要:實際上并不存在什么構造函數,只存在對于函數的構造調用發生構造函數的調用時,會自動執行下邊的操作創建一個全新的對象。說明綁定的優先級高于硬綁定。
原文閱讀
??js中的this是很容易讓人覺得困惑的地方,這篇文章打算說一下this綁定的幾種情況,相信可以解決大部分關于this的疑惑。
1.幾種綁定規則this是在運行的時候綁定的,不是在編寫的時候綁定的,函數調用的方式不同,就可能使this所綁定的對象不同。
??函數調用的位置對this的指向有著很大的影響,但卻不是完全取決于它。下面是幾種this的綁定規則:
1.1.默認綁定??默認規則的意思就是在一般情況下,如果沒有別的規則出現,就將this綁定到全局對象上,我們看如下代碼:
function foo() { var a = 3; console.log( this.a ); } var a = 2; foo(); //2
??這段代碼中,this是被默認綁定到了全局對象上,所以this.a得到的是2。我們如何判斷這里應用了默認綁定呢?foo在調用的時候直接使用不帶任何修飾的函數引用,只能使用默認綁定。有人會誤認為結果是3,this常有的幾種錯誤理解之一就是認為this指向當前函數的詞法作用域,this與詞法作用域以及作用域對象是完全不同的東西,作用域對象是在引擎內部的,js代碼是無法訪問的。還有本文我們不討論嚴格模式下的情況,嚴格模式這里的this會綁定到undefined。
1.2.隱式綁定??如果在調用位置有上下文對象,說簡單點就是這個函數調用時是用一個對象.出來的。就像下邊這樣,它就遵循隱式綁定:
function foo() { console.log(this.a); } var obj = { a: 2, foo: foo } var a = "opps, gloabl"; //全局對象的屬性 obj.foo(); //2 var bar = obj.foo; bar(); //"opps, gloabl"
??第9行代碼,就是函數在調用的時候,是用前邊的對象加上.操作符調用出來的,此時就用到了隱式綁定規則,隱式綁定規則會將函數調用中的this綁定到這個上下文對象,此時的this.a和obj.a是一樣的。
??而隱式綁定會出現一個問題,就是隱式丟失,上邊的第10行代碼,是新建一個foo函數的引用,即bar,在最后一行調用的時候,這個函數不具有上下文對象,此時采用默認綁定規則,得到的結果自然是opps, gloabl;
??綁定丟失也會發生在函數作為參數傳遞的情況下,即傳入回調函數時,因為參數傳遞就是一種隱式賦值,看如下代碼:
function foo() { console.log( this.a ); } function doFoo(fn) { fn(); //在此處調用,參數傳遞是隱式賦值,丟失this綁定 } var obj = { a: 2, foo: foo }; var a = "opps, global"; doFoo( obj.foo ); //看似是隱式綁定,輸出opps, global
??javascript環境中內置的函數,在具有接受一個函數作為參數的功能的時候,也會發生像上邊這種狀況。例如setTimeout函數的實現就類似于下邊的偽代碼:
function setTimeout(fn, delay) { //等待delay毫秒 fn();//在此處調用 }
??所以回調函數丟失this綁定是非常常見的,后邊我們再看如何通過固定this來修復這個問題。
1.3.顯式綁定??在此之前,相信你已經用過很多次apply和call函數了,使用這兩個函數可以直接為你要執行的函數指定this,所以這種方式稱為顯式綁定。
function foo() { //顯式綁定this,這種方式依然無法解決綁定丟失的問題 console.log( this.a ); } var obj = { a: 2 }; foo.call( obj ); //2
??通過像上邊這樣調用,我們可以將foo的this強制綁定到obj上。如果給call傳入的是一個基本類型數據,這個基本類型數據將會被轉換成對應的基本包裝類型。不過這種方式依然無法解決上邊的丟失綁定問題。
1.3.1.硬綁定??為了解決這個問題,我們可以寫像下邊這樣的代碼:
function foo() { console.log( this.a ); } var obj = { a: 2 }; var bar = function() { //創建一個包裹函數,以確保obj的綁定 foo.call( obj ); }; bar(); //2 setTimeout( bar, 100 ); //2 bar.call( window ); //2
??上邊這樣的函數確實解決了綁定丟失的問題,每次調用bar就可以確保obj的綁定,不過還不能為函數傳參,而且這種方法復用率低,所以又出現了這樣的輔助綁定函數:
function foo(something) { console.log( this.a, something ); return this.a + something; } function bind(fn, obj) { //輔助綁定函數 return function() { return fn.apply( obj, arguments ); }; } var obj = { a: 2 }; var bar = bind( foo, obj ); var b = bar(3); console.log(b);
??因為這種模式很常用,所以ES5內置了這個方法,就是bind,bind(...)返回一個硬編碼的新函數,將你指定的對象綁定到調用它的函數的this上。
function foo(something) { console.log( this.a, something ); return this.a + something; } var obj = { a: 2 }; var bar = foo.bind( obj ); //bind返回一個綁定到obj上的新函數 var b = bar(3); console.log(b); var a = "window"s a"; foo("!"); //對原來的函數不產生影響1.3.2.API調用參數指定this
??許多第三方庫里的函數,以及許多語言內置的函數,都提供了一個可選的參數用來指定函數執行的this:
function foo(el) { console.log( el, this.id ); } var obj = { id: "awesome" }; [1, 2, 3].forEach( foo, obj ); //forEach的第二個參數就是用來設置this1.4.new綁定
js中的所謂的構造函數,其實和一般的普通函數沒有什么區別,并不具有特殊性,它們只是被new操作符調用的普通函數而已。實際上并不存在什么構造函數,只存在對于函數的構造調用
發生構造函數的調用時,會自動執行下邊的操作:
創建一個全新的對象。
這個對象會被執行[[Prototype]]連接。
這個新對象會綁定到函數調用的this。
執行這個函數里的代碼。
如果函數沒有返回其他對象,則自動返回這個新對象。
這個在執行new操作的時候對this的綁定就叫做new綁定。
function fun() { this.a = 1; this.b = 2; } var instance = new fun(); console.log(instance.a);1.5.箭頭函數的this
??ES6中的箭頭函數是無法使用以上幾種規則的,它是根據外層的作用域來決定this,即取決于外層的函數作用域或全局作用域,而且箭頭函數的綁定無法修改,即使是new綁定也不可以。
function foo() { return (a) => { console.log( this.a ); } } var obj1 = { a: 2 }; var obj2 = { a: 3 }; var bar = foo.apply(obj1); bar.apply(obj2); //22.綁定規則的優先級
??前邊我們已經說了this的幾種綁定規則,當函數調用的位置可以使用多條綁定規則的時候,我們就需要確定這幾種規則的優先級。
function foo() { console.log( this.a ); } var obj1 = { a: 2, foo: foo }; var obj2 = { a: 3, foo: foo } obj1.foo(); //2 obj2.foo(); //3 obj1.foo.call( obj2 ); //3 obj2.foo.call( obj1 ); //2
??從上邊的代碼可以看出來,顯式綁定的優先級要高于隱式綁定,下邊再看看顯式綁定和new綁定的優先級:
function foo(something) { this.a = something; } var obj1 = {}; var bar = foo.bind( obj1 ); bar(2); console.log( obj1.a ); //2 var baz = new bar(3); console.log( obj1.a ); //2 console.log( baz.a ); //3
??仔細看這段代碼,bar是foo綁定到obj1上返回的一個函數,對這個函數進行new操作,并傳入新的a值,發現改變的是新對象baz的屬性,和obj1已經脫離關系。說明new綁定的優先級高于硬綁定。
??綜上所述,我們在遇到this時,如果不是箭頭函數,就可以以這種順序判斷它的指向:
如果函數在new中調用,綁定到新建的對象。
函數通過call或apply或者硬綁定調用,this綁定到指定的對象上。
函數在某個上下文對象中調用,綁定到這個上下文對象上。
采用默認綁定規則。
??以上就是這篇博客的所有內容了,如果有什么理解的不對的地方歡迎討論,這里是我的博客以及github,歡迎來訪。
參考書籍:《你不知道的JavaScript(上卷)》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88324.html
摘要:理解的函數基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統的類繼承還要強大。中文指南基本操作指南二繼續熟悉的幾對方法,包括,,。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家帶來幫助....(據說是阿里的前端妹子寫的) this 的值到底...
摘要:出于這個原因,該函數返回的,所以在這里指的是,所以返回的是第一個說明關鍵字通常在對象的構造函數中使用,用來引用對象。重寫無法重寫,因為它是一個關鍵字。結論,表示當前的上下文對象是一個對象,可以調用對象所擁有的屬性,方法。 在《javaScript語言精粹》這本書中,把 this 出現的場景分為四類,簡單的說就是: 有對象就指向調用對象 沒調用對象就指向全局對象 用new構造就指向新對...
摘要:也就是說當使用后,當前執行上下文中的對象已被替換為,后續執行將以所持有的狀態屬性繼續執行。借用的方法替換的實例去調用相應的方法。實現引用類型的繼承其實沒有類這一概念,我們平時使用的等嚴格來說被稱作引用類型。 call 方法:object.method.call(targetObj[, argv1, argv2, .....]) apply 方法:object.method.apply(...
摘要:再來看一個小的示例淘寶騰訊淘寶為什么輸出的依然是淘寶呢調用的是對象中的方法,方法里面有一個定時器,而定時器的一個參數是這里的指的就是的對象,然后方法里面有調用了,但是定時器中的指的是對象,所以最終調用的是對象中。 1.看前熱身 看一段代碼 var name = javascript; var obj = { name:js, foo:f...
閱讀 2444·2021-11-19 09:59
閱讀 1973·2019-08-30 15:55
閱讀 930·2019-08-29 13:30
閱讀 1330·2019-08-26 10:18
閱讀 3081·2019-08-23 18:36
閱讀 2382·2019-08-23 18:25
閱讀 1156·2019-08-23 18:07
閱讀 430·2019-08-23 17:15