摘要:在傳統的面向類的語言中,構造函數是類中的一些特殊方法,使用初始化類是會調用類中的構造函數。
在上一節中我們詳細介紹了this的兩種綁定方式,默認綁定和隱式綁定,在這一節我們繼續介紹this的另外兩種綁定方式顯示綁定和new綁定。那么,我們要解決的問題當然就是上一節中我們提到的:this丟失!
顯式綁定在隱式綁定中,我們必須在一個對象的內部包含一個指向函數的屬性,并通過這個屬性間接引用函數,從而把this間接綁定到這個對象上。那么如果我們不想在每個對象內部包含函數引用,而想在每個對象上強制調用函數,該怎么做呢?
這時就需要 call(綁定this, 其他參數...)和apply(綁定this, 其他參數...)方法出場了,這兩個方法的第一個參數都是給this準備的,不同之處在于其他參數的形式上,他們兩的其他參數對比如下:
call(綁定this, "參數1","參數2","參數3","參數4"); apply(綁定this, ["參數1","參數2","參數3","參數4"]);
apply的其他參數是以數組序列形式存在的,它會在執行時將其解析成單個的參數再依次的傳遞到調用的函數中,這有什么用處呢?加入我們有一個數組:
var arr = [1,2,3,4,5,6];
現在我要找到其中的最大值,當然這里有很多方法了。既然這里講到apply那么我們就用apply方法來解決這個問題。如果想要找到一組數中最大的一個,有一個簡單的方法,使用Math.max(...)。但是,該方法并不能找出一個數組中的最大值,也就是說:
Math.max(1,2,3,4,5); // 可以找到最大值5 Math.max([1,2,3,4,5]); // 這就不行了,因為不接受以數組作為參數
我們的做法就是通過:
Math.max.apply(null, [1,2,3,4,5]); //得到數組中的最大值5
還有很多其他方面的用處,比如push等等,似乎有點跑題了!!!
不過我想說的就是通過call()和apply()這兩種方法我們可以顯式的綁定this到指定的對象!
function foo(){ console.log(this.a); } var obj = { a: 2 } foo.call(obj);//2
但是,顯式綁定仍舊無法解決this丟失綁定的問題。
顯式綁定的一個變種可以解決這個問題。
function foo(){ console.log(this.a); } var obj = { a: 2 } var bar = function(){ foo.call(obj); } bar();// 2 setTimeout(bar, 100); // 2 bar.call(window); // 2
看看它是如何工作的:我們創建了一個函數bar(),并在他的內部手動調用foo.call(obj)。因此,強制把foo的this綁定到了obj,無論之后如何調用函數bar,它總會手動在obj上調用foo。這樣的形式我們稱之為硬綁定。
硬綁定的典型應用場景就是創建一個包裹函數,負責接收參數并返回值:
function foo(something){ console,log(this.a, something); return this.a + something; } var obj = { a: 2 } var bar = function(){ return foo.apply(obj, arguments); } var b = bar(3); //2, 3 console.log(b); //5
另一個常用的方法是創建一個可以重復使用的輔助函數:
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(); //2, 3 console.log(b); //5
硬綁定是一種非常常用的模式,所以ES5提供了內置的方法Function.prototype.bind,它的用法如下:
function foo(something){ console,log(this.a, something); return this.a + something; } var obj = { a:2 } var bar = foo.bind(obj); var b = bar(3); //2, 3 console.log(b); //5
bind(...)會返回一個硬編碼的心函數,它會把指定的參數設置為this的上下文并調用原始函數。
new 綁定第四條規則,也是最后一條規則,在講解他之前我們首先要澄清一個非常常見的關于javascript中函數和對象的誤解。
在傳統的面向類的語言中,“構造函數”是類中的一些特殊方法,使用new初始化類是會調用類中的構造函數。通常的形式是這樣:
someThinges = new MyClass(...)
javascript中也有個new操作符,但javascript中的new操作符的機制與面向類的語言完全不同。首先我們重新定義一下JavaScrit中的“構造函數”。在Javascript中,構造函數只是一些使用new操作符時被調用的函數。它并不會屬于某個類,也不會實例化一個類。實際上它甚至都不能說是一種特殊的函數類型,它們只是被new操作符調用的普通函數而已。
舉例來說,思考一下Number()作為構造函數時的行為,ES5.1中這樣描述它:
Number構造函數 當Number在new表達式中被調用時,它是一個構造函數:它會初始化新建的對象。
所以,包括內置對象函數在內的所有函數都可以用new來調用,這種函數被稱為構造函數調用,這有個非常細微的區別:實際上并不存在所位的“構造函數”,只有對于函數的“構造調用”。
使用new來調用函數,會自動執行下面的操作:
創建一個全新的對象
這個新對象會被執行[[prototype]]連接(之后會細說)
這個新對象會綁定到函數調用的this
如果函數沒有返回其他對象,那么new表達式中的函數會自動返回這個對象。
function foo(a){ this.a = a } var bar = new foo(2); console.log(bar) // foo {a: 2} console.log(bar.a); //2
使用new 來調用foo(...)時,我們會構造一個新的對象,并把它綁定到foo(...)調用中的this上。new是最后一種可以影響函數調用時this綁定行為的方法。我們稱之為new綁定。
箭頭函數我們之前介紹的四條規則已經可以包含所有正常是有的函數。但是在ES6中介紹了一種無法使用這些規則的特殊函數類型:箭頭函數
箭頭函數不是使用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上,而且箭頭函數的綁定無法被修改!
箭頭函數最常用與回調函數中,例如事件處理器或者定時器:
function foo(){ setTimeot(()=>{ //這里的this在詞法上繼承自foo(),也就是說只要foo()綁定到了obj1上,箭頭函數的this也就綁定到了obj1上 console.log(this.a) },100) } var obj1 = { a: 2 } foo.call(obj1); //2
箭頭函數可以像bind(..)一樣確保函數的this被綁定到指定的對象,此外,其重要性還體現在他用更常見的詞法作用域取代了傳統的this機制。實際上在,ES6之前我們就已經使了用一種幾乎和箭頭函數完全一樣的模式。
function foo(){ console.log(this); //Object {a: 2} var self = this; //詞法作用域捕獲this setTimeout(function(){ console.log(this); // Window {external: Object, chrome: Object, document: document, obj1: Object, obj2: Object…} console.log(self.a); }, 100); } var obj1 = { a: 2 } foo.call(obj1); //2
我分別在這段代碼中foo()的內部,和setTimeout()的內部加了兩行代碼console.log(this),當調用foo()函數并將其this綁定到obj1上時(即執行foo.call(obj1)),foo()內的this此時是Object {a: 2},說明foo()函數中的this已經綁定到了obj1上,setTimeout()內的結果是Window...,如果你看了上一節《this全面解析(一)》的內容應該會很好理解,因為在setTimeout()方法中,函數傳參相當于隱式賦值,調用方式自然運用默認規則,setTimeout()方法中函數的this指向window。為了讓我們得到預期的結果,我們將foo()中的this保存下來(即var self = this),然后通過詞法作用域的在setTimeout()方法中的函數中引用self變量。讀者可以自行測試,如果不這樣做得出的結果會是什么(undifined嗎?自行驗證一下吧!)
好吧!一不小心又啰嗦的講了這么多。雖然,self = this和箭頭函數看起來都可以取代bind(),但本質上來說,他們想取代的是this機制。
如果要判斷一個運行中函數的this綁定,就需要找到這個函數的直接調用位置。找到后就可以順序應用下面這四條規則來判斷this的綁定對象
是否由new調用?綁定到新創建的對象
是否由call()或apply()調用?綁定到指定的對象
是否由上下文對象調用?綁定到那個上下文對象
默認:嚴格模式undifined,非嚴格綁定到全局對象
ES6中的箭頭函數不會使用四條標準的綁定規則,而是根據詞法作用域來決定this,具體來說,箭頭函數會繼承外層函數調用的this綁定(無論this綁定到了什么),這其實和ES6之前代碼中的self = this 機制一樣。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79002.html
摘要:當我們不想再對象內部間接包含引用函數,而像在某個對象上強制調用函數。我們可以用中內置的和的方法來實現,這兩個方法的第一個參數是一個對象,是給準備的,接著再調用函數時將其綁定到。 this是什么 在javascript中,每個執行上下文可以抽象成一組對象showImg(https://segmentfault.com/img/bVuKR7); 而this是與執行上下文相關的特殊對象,任何...
摘要:關于的全棉解析上的文章地址判斷函數是否在中調用綁定如果是的話綁定的是新創建的對象。顯而易見,這種方式可能會導致許多難以分析和追蹤的。默認在嚴格模式下綁定到,否則綁定到全局對象。 關于this的全棉解析(上)的文章地址 判斷this 函數是否在new中調用(new綁定)?如果是的話this綁定的是新創建的對象。 bar = new foo() 函數是否通過call、apply(顯式綁定...
摘要:關于的全面解析下頁面鏈接的調用位置調用位置就是函數在代碼中被調用的位置而不是聲明的位置,尋找調用位置就是尋找函數被調用的位置,最重要的是分析調用棧就是為了到達當前執行位置所調用的所有函數。因此,調用函數時被綁定到這個對象上,所以和是一樣的。 關于this的全面解析(下)頁面鏈接 this的調用位置 調用位置就是函數在代碼中被調用的位置(而不是聲明的位置),尋找調用位置就是尋找函數被調用...
摘要:箭頭函數的尋值行為與普通變量相同,在作用域中逐級尋找。題目這次通過構造函數來創建一個對象,并執行相同的個方法。 我們知道this綁定規則一共有5種情況: 1、默認綁定(嚴格/非嚴格模式) 2、隱式綁定 3、顯式綁定 4、new綁定 5、箭頭函數綁定 其實大部分情況下可以用一句話來概括,this總是指向調用該函數的對象。 但是對于箭頭函數并不是這樣,是根據外層(函數或者全局)作用域(...
摘要:所以也就是說在沒有的基礎上,執行代碼會在串池中創建一個,也會在堆內存中再出來一個。不可變性的優點安全性字符串不可變安全性的考慮處于兩個方面,數據安全和線程安全。 摘要: String基本特性,String源碼,為什么String不可變? 前言 基于字符串String在java中的地位,關于String的常識性知識就不多做介紹了,我們先來看一段代碼 public class Test {...
閱讀 1777·2023-04-25 21:50
閱讀 2425·2019-08-30 15:53
閱讀 772·2019-08-30 13:19
閱讀 2751·2019-08-28 17:58
閱讀 2473·2019-08-23 16:21
閱讀 2706·2019-08-23 14:08
閱讀 1382·2019-08-23 11:32
閱讀 1446·2019-08-22 16:09