http://blog.csdn.net/u014267183/article/details/52610600es6
以前看了點es6的箭頭函數,爲了搞懂箭頭函數的this,看了不少文章,也順便看了幾個綁定函數,發現不少之前沒注意的問題,收穫很多。面試
以前就在網上的筆試題中看過用js實現bind()函數,沒怎麼在乎,覺得既然都是用來進行上下文綁定的,用call或者apply應該就能實現。如今看,我仍是圖樣圖森破。數組
先來說一下call()和apply()吧,對於這兩個函數,我是看本身的書學習的,學的時候沒覺的有什麼問題,可是我查了一下網上的關於call()和apply()的文章,尼瑪啊,這說都是些什麼啊!!看着真費勁。瀏覽器
其實,call()和apply()就是改變函數的執行上下文,也就是this值。他們兩個是Function對象的方法,每一個函數都能調用。他們的第一個參數就是你要指定的執行上下文,第二個用來傳遞參數(說第二個不許確,應該說第二部分,由於參數能夠傳多個),也就是傳給調用call和apply方法的函數的參數。說白了,就是調用函數,可是讓它在你指定的上下文下執行,這樣,函數能夠訪問的做用域就會改變。下面看點代碼:app
function apply1(num1, num2){
return sum.apply(this, [num1, num2]);
}
function call1(num1, num2){
return sum.call(this, num1, num2);函數
}工具
這裏,咱們執行環境傳的是this,也就是說沒改變函數的執行上下文。這兩段代碼,只是想告訴你call和apply的區別。post
call的第二部分參數要一個一個傳,apply要把這些參數放到數組中。這就是他們的區別,真的就這麼點區別!!!學習
而後,不得不說的一點:它們的第二個參數均可以傳arguments。測試
—————————————————————————————————————————————————————————————————————————————
下面來說bind()函數,bind()是es5中的方法,他也是用來實現上下文綁定,看它的函數名就知道。bind()和call與apply不一樣。bind是新建立一個函數,而後把它的上下文綁定到bind()括號中的參數上,而後將它返回。
因此,bind後函數不會執行,而只是返回一個改變了上下文的函數副本,而call和apply是直接執行函數。
下面代碼能夠反映出這點,並且也顯示了bind的用法(後面的代碼皆取自張鑫旭大神的博客)
var button = document.getElementById("button"), text = document.getElementById("text"); button.onclick = function() { alert(this.id); // 彈出text }.bind(text);
但因爲ie6~ie8不支持該方法,因此若想在這幾個瀏覽器中使用,咱們就要模擬該方法,這也是面試常考的問題,模擬的代碼以下:
if (!function() {}.bind) { Function.prototype.bind = function(context) { var self = this , args = Array.prototype.slice.call(arguments); return function() { return self.apply(context, args.slice(1)); } }; }就是這段代碼,糾正了我長久以來的一個誤區。下面來說一下這段代碼
首先,咱們判斷是否存在bind方法,而後,若不存在,向Function對象的原型中添加自定義的bind方法。
這裏面var self = this這段代碼讓我很困擾,按理說,prototype是一個對象,對象的this應該指向對象自己,也就是prototype,但真的是這樣嗎。看看下面的代碼:
function a(){};
a.prototype.testThis = function(){console.log(a.prototype == this);};
var b = new a();
b.testThis();//false
顯然,this不指向prototype,而通過測試,它也不指向a,而指向b。因此原型中的this值就明朗了。指向調用它的對象。
Array.prototype.slice.call(arguments);接下來就是上面這段代碼,它會將一個類數組形式的變量轉化爲真正的數組。爲啥呢,其實書上並無說slice還有這樣的用法,也不知道是誰發明的。slice的用法能夠順便上網查一下,就能查到。但要更正一點,網上的介紹說slice有兩個參數,第一個參數不能省略。然而我不知道是我理解的問題仍是咋地,上面這段代碼tmd不就是典型的沒傳參數嗎!!!arguments是傳給call的那個上下文,前面講過,不要弄混(因爲arguments本身沒有slice方法,這裏屬於借用Array原型的slice方法)。並且通過測試,若果你不給slice傳參數,那就等於傳了個0給它,結果就是返回一個和原來數組如出一轍的副本。
這以後的代碼就很好理解,返回一個函數,該函數把傳給bind的第一個參數當作執行上下文,因爲args已是一個數組,排除第一項,將以後的部分做爲第二部分參數傳給apply,前面講過apply的用法。
如此,咱們本身的這個bind函數的行爲就同es5中的bind同樣了。