JavaScript裡call,apply,bind方法簡介

來源:互聯網
上載者:User

JavaScript裡call,apply,bind方法簡介

JavaScript裡call,apply,bind方法不太容易理解,其實背後的思想並不算非常複雜,希望本文能幫你更好地瞭解這3個很像,而且看似很神秘的方法。

非要用一個關鍵字來點明它們的背後思想的精髓的話,關鍵字就是:this

 

因為通常程式員對C++比較熟,先借用C++,簡單說一下this。

類的成員函數裡,都可以用this來訪問當前類的成員,但問題是成員函數的參數並沒有this這個參數,比如:

 

Animal a;a.eat(); a.eat("meat"); 
Animal的對象調用無參和單參數成員函數eat。在eat方法裡可以用this訪問其他成員。可問題是eat是個"無參"或“單形參”的方法,並沒有一個叫this的形參。

 

原因在於編譯器會對類的成員函數進行擴充。

 

a.eat();        //編譯器會重寫成:Animal::eat(&a);a.eat("meat");  //編譯器會重寫成:Animal::eat(&a, "meat");
類的每個成員函數(除static成員函數。因為static是屬於類而非對象的,static和this天生語義不同)都有一個額外的,隱含的形參this。調用成員函數時,形參this初始化為調用函數的對象的地址。

 


扯遠了,拉回主題。。。先看JavaScript中的Function.prototype中的call方法

就像C++一樣,函數或方法的接收者是綁定到this的,但有時需要使用自訂接收者來調用函數或方法。call方法就是用於改變this。

 

function Class1() {     this.name = "class1";     this.showName = function() { alert(this.name); } } function Class2() {     this.name = "class2"; } var c1 = new Class1(); var c2 = new Class2(); 

如果你直接在Class2的對象上調用showName一定報錯:

 

c2.showName();    //Error

編譯器會重寫成:Class2::showName(&c2),顯然Class2裡沒有showName方法將報錯,這一點不奇怪所有人都能理解。


但showName方法太誘人,無論如何都想用呢?有兩個辦法,一個自然是在Class2中自己定義一個showName方法,這沒什麼不好,但有時顯得比較麻煩,且重複代碼維護起來也是個問題。另一種解決方案就是用call:

 

c1.showName.call(c2);//class2

 

編譯器會重寫成:Class1::showName(&c2),隱藏的形參this本來是預設指向Class1的對象的,現在call方法將this改變為指向Class2的對象。

這樣就將Class1中定義的showName方法實體應用到Class2的對象上,即用c2的上下文替換c1的上下文

 

理解了call之後,apply就迎刃而解,因為兩者本質是一樣的,區別在於外在表現:

object.call(this, arg1,arg2,arg3) == object.apply(this, arguments)

從第二個參數起,call方法參數將依次傳遞給借用的方法作參數,而apply直接將這些參數放到一個數組中再傳遞,最後借用方法的參數列表是一樣的。

因為上面這個例子的showName方法沒有參數,因此用call和apply是一樣的:

 

c1.showName.call(c2);//class2c1.showName.apply(c2);//class2

最後是 bind。call和apply是改變this,bind同樣不例外,但改變this後還將方法返回,因此bind準確的語義是:獲得具有確定接收者的方法。

 

它本質上是得到一個方法,方法可以直接調用,也可以被當做參數傳遞給高階函數。

如果直接調用那上面這個例子用bind可以改成:

 

c1.showName.call(c2);//class2c1.showName.apply(c2);//class2c1.showName.bind(c2)();//class2

 

看到bind後面的一對小括弧了嗎?如果沒有那對小括弧,c1.showName.bind(c2); 僅僅表示一個方法,然後就沒有然後了。加上那對小括弧就是直接調用"通過bind為方法綁定接收者後返回的方法"

如果只能直接調用,那用call和apply即可,bind就沒有存在的價值了,因此另一個作用就是當做參數傳遞給高階函數,一個常用的例子就是用bind實現函數柯裡化(將函數與其參數的一個子集綁定的技術稱為函數柯裡化):

 

function simpleURL(protocol, domain, path) {    return protocol + "://" + domain + "/" + path;}

上面是一個非常普通的產生URL地址的函數。但你覺得其實前兩個參數通常是固定不變的,無非是第三個參數要變來變去,你可以這樣:

 

var urls = paths.map(function(path) {    return simpleURL("http", "csdn", path);//前兩個參數都固定,只有第3個參數會變});
用bind可以簡化上述代碼:

 

 

var urls = paths.map(simpleURL.bind(null, "http", "csdn"));
bind的第一個參數為方法的接收者,由於simpleURL沒有this,因此設為null即可,表示忽略接收者。這樣bind將單個參數path調用simpleURL函數委託到simpleURL("http", "baidu", path)函數。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.