本文章記錄本人在學習 JavaScript 中看書理解到的一些東西,加深記憶和而且整理記錄下來,方便以後的複習。編程
說道代碼複用,通常都會涉及到對象繼承。在js
中有許多能夠選擇的繼承方法。這些方法對於學習和理解多種不一樣的模式有很大的好處,由於它們有助於提供對語言的掌握程度。數組
可是在開發的過程當中,並非全部的代碼複用都會使用到繼承。其中一部緣由在於,事實上使用的js
庫可能以這樣的或那樣的方式解決了該問題。而另外一方面的緣由就在於不多須要在js
中創建長並且複雜的繼承鏈。在靜態強類型語言中,繼承能夠能是惟一複用代碼的方法。在js
中,常常有更加簡潔並且優美的方法。包括:借用方法、綁定、複製屬性以及從多個對象中混入屬性等許多方法。閉包
混入是針對經過屬性複製實現繼承的思想作進一步的擴展,mix-in
模式並非複製一個完整的對象,而是從多個對象中複製出任意的成員並將這些成員組合成新的對象。app
實現mix-in
:函數
function mix() { var arg, prop, child = {}; for (arg = 0; arg < arguments.length; arg += 1) { for (prop in arguments[arg]) { if (arguments[arg].hasOwnProperty(prop)) { child[prop] = arguemnts[arg][prop]; } } } return child; }
mix-in
實現很是簡單,只須要遍歷每一個參數,而且複製出傳遞給該函數的每一個對象中的每一個屬性。學習
有的時候,咱們只須要對象中的一兩個方法,可是又不想和對象造成父-子繼承關係。只是想是用所須要的方法,而不但願繼承全部那些永遠用不到的屬性和方法。在這種狀況下,能夠經過使用借用方法模式來實現。測試
而這個方法是受益於call()
和apply()
的。js
中函數也是對象,而且它們自身也存在一些屬性和方法,好比call
和apply()
。this
使用call()
和apply()
分別借用方法:prototype
// call notmyobj.doStuff.call(myobj, param1, p2, p3); // apply notmyobj.doStuff.apply(myobj, [param1, p2, p3]);
在知道notmyobj
對象上有doStuff
方法的狀況下,又想不繼承notmyobj
來使用doStuff
方法。這個使用call()
和apply()
就派上用場了。指針
還有一個場景是常用到借用方法的。那就是借用數組方法。由於數據具備許多很強大的方法,並且有時候須要操做arguments
的時候會用上。可是arguments
是一個僞數組,不具備原生數組強大的方法。這個使用借用方法就派上用場了:
function f() { var args = [].slice.call(arguemnts, 1, 3); return args; }
考慮到借用方法不是經過調用call
和apply()
就是經過簡單的複製,在借用方法的內部,this
所指向的對象是基於調用表達式而肯定的,可是有的時候「鎖定」this
的值,或者將其綁定到特定的對象而且預先肯定該對象。
舉一栗子:
var one = { name: 'object', say: function (greet) { return greet + ", " + this.name; } }; // 測試 one.say('hi'); // hi, object
接着另外一對象two
中沒有say()
方法,借用one
的say()
方法:
var two = { name: 'another object' } // 測試 one.say.call(two, 'hi'); // hi, another object
在上面的例子中,借用的say()
方法的this
是指向two
的。可是在什麼樣的場景中,應該將函數指針賦值給一個全局變量,或者將該函數作爲回調函數來傳遞?在客戶端編程中有許多事件和回調,所以確實發生了許多這樣混淆的事件。
舉一個栗子:
// 給變量賦值 // this 將指向全局變量 var say = one.say; say('hello') // hello, undefined // 做爲回調傳遞 var yetanother = { name: 'yet another object', method: function (callback) { return callback('hola'); } }; // 測試 yetanother.method(one.say) // holla, undefined
在上面的兩種狀況下,say()
方法的this
值都是指向全局對象。並且整個代碼都沒法按照預期來運行。爲了修復(綁定)對象與方法之間的關係。可使用一個簡單的函數來實現:
function bind(o, m) { return function () { return m.apply(o, [].slice.call(arguments)) }
上面的bind()
方法接受兩個參數。一個是對象o
,另外一個是方法m
,並將二者綁定起來。而後返回一個函數。
使用bind()
來解決問題:
var twosay = bind(two, one.say); twosay('yo'); // yo another object
奢侈的擁有綁定所須要輔助的代價就是額外的閉包的開銷。
在ECMAScript5
中給Function.protoype
添加了一個bind()
方法,使得bind()
與call()、apply()
同樣簡單易用。
可是在不支持ECMAScript5
的運行環境下,咱們能夠本身實現一個bind()
方法(來自 MDN):
if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; }
而後使用自帶的bind()
方法來重寫一下上面的栗子:
var twosay = bind(two, one.say); twosay('Bonjour'); // yo another object
最後,若是文章有什麼錯誤和疑問的地方,請指出。與sf各位共勉!