今天看博客時,看到了這樣的一段js代碼:數組
var bind = Function.prototype.call.bind(Function.prototype.bind);
我想忽然看到這樣的一段代碼,即便js能力再強的人,可能也須要花點時間去理解。像我這樣的菜鳥就更不用說了。其實,原文已經對這端代碼作出瞭解釋,但我仍是想用個人想法去解釋這段代碼。app
上面那段代碼涉及到了call、bind,因此我想先區別一下call、apply、bind的用法。這三個方法的用法很是類似,將函數綁定到上下文中,即用來改變函數中this的指向。舉個例子:函數
var zlw = { name: "zlw", sayHello: function (age) { console.log("hello, i am ", this.name + " " + age " years old"); } }; var xlj = { name: "xlj", }; zlw.sayHello(24);// hello, i am zlw 24 years old
下面看看call、apply方法的用法:this
zlw.sayHello.call(xlj, 24);// hello, i am xlj 24 years old zlw.sayHello.apply(xlj, [24]);// hello, i am xlj 24 years old
結果都相同。從寫法上咱們就能看出兩者之間的異同。相同之處在於,第一個參數都是要綁定的上下文,後面的參數是要傳遞給調用該方法的函數的。不一樣之處在於,call方法傳遞給調用函數的參數是逐個列出的,而apply則是要寫在數組中。spa
咱們再來看看bind方法的用法:prototype
zlw.sayHello.bind(xlj, 24)(); //hello, i am xlj 24 years old zlw.sayHello.bind(xlj, [24])(); //hello, i am xlj 24 years old
bind方法傳遞給調用函數的參數能夠逐個列出,也能夠寫在數組中。bind方法與call、apply最大的不一樣就是前者返回一個綁定上下文的函數,然後二者是直接執行了函數。因爲這個緣由,上面的代碼也能夠這樣寫:code
zlw.sayHello.bind(xlj)(24); //hello, i am xlj 24 years old zlw.sayHello.bind(xlj)([24]); //hello, i am xlj 24 years old
bind方法還能夠這樣寫 fn.bind(obj, arg1)(arg2) 。對象
用一句話總結bind的用法:該方法建立一個新函數,稱爲綁定函數,綁定函數會以建立它時傳入bind方法的第一個參數做爲this,傳入bind方法的第二個以及之後的參數加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數。blog
如今回到開始的那段代碼:博客
var bind = Function.prototype.call.bind(Function.prototype.bind);
咱們能夠這樣理解這段代碼:
var bind = fn.bind(obj)
fn 至關於 Function.prototype.call , obj 至關於 Function.prototype.bind 。而 fn.bind(obj) 通常能夠寫成這樣 obj.fn ,爲何呢?由於 fn 綁定了 obj , fn 中的 this 就指向了 obj 。咱們知道,函數中 this 的指向通常是指向調用該函數的對象。因此那段代碼能夠寫成這樣:
var bind = Function.prototype.bind.call;
你們想想 Function.prototype.call.bind(Function.prototype.bind) 返回的是什麼?
console.log(Function.prototype.call.bind(Function.prototype.bind)) // call()
返回的是 call 函數,但這個 call 函數中的上下文的指向是 Function.prototype.bind 。這個 call 函數能夠這樣用
var bind = Function.prototype.call.bind(Function.prototype.bind); var zlw = { name: "zlw" }; function hello () { console.log("hello, I am ", this.name); } bind(hello, zlw)() // hello, I am zlw
你們可能會感到疑惑,爲何是這樣寫 bind(hello, zlw) 而不是這樣寫 bind(zlw, hello) ?既然 Function.prototype.call.bind(Function.prototype.bind) 至關於 Function.prototype.bind.call ,那麼先來看下 Function.prototype.bind.call 怎麼用。 call 的用法你們都知道:
Function.prototype.bind.call(obj, arg)
其實就至關於 obj.bind(arg) 。咱們須要的是 hello 函數綁定對象 zlw ,即 hello.bind(zlw) 也就是 Function.prototype.bind.call(hello, zlw) ,因此應該這樣寫 bind(hello, zlw) 。
如今又有一個疑問,既然 Function.prototype.call.bind(Function.prototype.bind) 至關於 Function.prototype.bind.call ,咱們爲何要這麼寫:
var bind = Function.prototype.call.bind(Function.prototype.bind);
而不直接這樣寫呢:
var bind = Function.prototype.bind.call;
先來看一個例子:
var name = "xlj"; var zlw = { name: "zlw" hello: function () { console.log(this.name); } };
zlw.hello(); // zlw
var hello = zlw.hello; hello(); // xlj
有些人可能會意外, hello() 的結果應該是 zlw 纔對啊。其實,將 zlw.hello 賦值給變量 hello ,再調用 hello() , hello 函數中的 this 已經指向了 window ,與 zlw.hello 再也不是同一個上下文,而全局變量 name 是 window 的一個屬性,因此結果就是 xlj 。再看下面的代碼:
var hello = zlw.hello.bind(zlw); hello(); // zlw
結果是 zlw ,這時 hello 函數與 zlw.hello 是同一個上下文。其實上面的疑惑已經解開了,直接這樣寫:
var bind = Function.prototype.bind.call;
bind 函數中的上下文已經與 Function.prototype.bind.call 中的不同了,因此使用 bind 函數會出錯。而這樣寫
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind 函數中的上下文與 Function.prototype.call.bind(Function.prototype.bind) 中是同樣的。
關於這個這段代碼的解釋這到這邊了,感受語言組織能力不是很好,文章寫得有些囉嗦了。文中可能會有錯誤,但願你們指正。