幾個名詞
鴨子模型:通俗的講就是如果它走起來像鴨子,叫起來像鴨子,那麼它就是鴨子。
鴨子模型主要是指導我們只關注物件的行為,而不是關注物件本身
多型:同一操作作用於不同物件上面,可以產生不同的解釋和不同的執行結果。換句話說,給不同的物件傳送不同的訊息,這些物件會根據這個訊息分別給出不同的反饋
多型背後的思想是將“做什麼”和“誰去做以及怎樣去做”分離開來,也就是將“不變的事物”與“可能改變的事物”分離開來
老生常談之this,apply,call
this指向
普通的方法定義之後,方法中的this
指向,是取決於方法執行的環境動態繫結的,而非方法被宣告時的環境。在es6中,使用箭頭函式,可以將this
固定下來,this
永遠指向的箭頭函式宣告時的this的指向。但是箭頭函式不能作為建構函式使用,不能使用arguments
物件
作為物件的方法呼叫
作為普通函式呼叫
var obj = { name: 'javascript', getName: function(){ return this.name } } obj.getName() // javascript 作為物件的方法進行呼叫,指向該物件 var getName = obj.getName getName() // undefined // 將物件的方法重新複製給新的變數, // 然後執行,getName作為普通函式呼叫,指向全域性的name複製程式碼
<html> <body> <div id="div1"> 我是一個div </div> </body> <script> window.id = 'window'; document.getElementIdById('div1').onclick = function(){ alert(this.id) // div1 var callback = function(){ alert(this.id) } callback() // window 作為普通方法進行呼叫,指向this全域性的window物件 } </script> </html>複製程式碼
構造器呼叫
使用
new
關鍵字生成一個物件,並將其建構函式的this
都指向這個生成的物件Function.prototype.cal
l和Function.prototype.apply
呼叫動態改變this的指向
“高逼格”的call,apply
call
和applay
的幾個用法和場景
改變this的指向
var obj1 = { name: 'obj1' } var obj2 = { name: 'obj2', getName: function(){ return this.name } } obj2.getName() // obj2 obj2.getName.apply(obj1) // obj1複製程式碼
對不支援bind的低版本瀏覽器提供相容方式
Function.prototype.bind(content){ var _self = this; return function(){ _self.apply(content,arguments) } }複製程式碼
借用其他物件的方法
最常用的就是
arguments
借用Array
的方法(function(){ Array.ptototype.push(arguments,4) console.log(arguments) // [1,2,3,4] })(1,2,3)複製程式碼
又是閉包,還有高階函式
通俗易懂的閉包
沒搞懂閉包肯定是因為沒太理解js的變數宣告週期。
- 全域性變數:變數的生命週期一直存在,不會被銷燬,除非我們主動去銷燬該變數
- 區域性變數:區域性變數一般存在在函式體內(es6有塊級作用域),當區域性變數的函式執行直接之後,該變數也就被銷燬了。
但是區域性變數存在一種情況,在函式執行完之後不會被銷燬,那就是該區域性變數還存在著引用(其他地方還在用這個變數),此時就出現了閉包。該區域性變數有留下來的理由了,所以也就延續下來了。
說到閉包,必會說到閉包的缺點——造成記憶體洩漏,當然這個記憶體洩漏時在使用閉包不當的時候才會出現這個問題,這個問題在本書的3.1.6章節中,作做了比較詳細的解釋(作者漂亮的甩了“一波鍋”)。有興趣的可以自己翻閱。
舉一個簡單的閉包的例子
function count(){
var a = 0;
return function(){
return a++;
}
}
var conutNum = count()
conutNum() // 0
conutNum() // 1
conutNum() // 2複製程式碼
沒那麼神祕的高階函式
滿足了一下條件之一的就是高階函式:
- 函式可以作為引數被傳遞
- 函式可以作為返回值輸出
函式作為引數被傳遞
平時使用的回撥函式都算。
再舉個例子:
// 從小到大排序 [1,3,2,5,4].sort(function(a,b){ return a-b }) // 從大到小排序 [1,3,2,5,4].sort(function(a,b){ return b-a })複製程式碼
函式作為返回值輸出
大部分的閉包都算。
再舉個單例模式的例子:
var getSingle = function(fn){ var ret; return function(){ return ret || ret = fn.apply(this,arguments) } } var createScript = getSingle(function(){ document.createElemet('script') }) var script1 = createScript() var script2 = createScript() script1 === script2 // true複製程式碼
高階函式的其他的一些應用
函式柯里化(function currying)
函式柯里化又稱部分求值,一個柯里化的函式首先會接受一些引數,接受了這些引數之後,該函式並不會立即求值,而是繼續返回另外一個函式,剛才傳入的引數在函式形成的閉包中被儲存起來。待到函式被真正需要求值的時候,之前傳入的所有引數都會被一次性用於求值
函式反柯里化(uncurrying)
函式節流
函式被頻繁呼叫(
resize
,mousemove
等事件),影響效能時,需要人為的限制函式的執行頻率,這種函式一般被稱為throttle
函式var throttle = function(fn, interval){ var _self = fn, timer, firstTime = true; return function(){ var args = arguments, _me = this; if(firstTime){ _self.apply(_me, args); retutn firstTime = false; } if(timer){ return false } timer = setTimeout(function(){ clearTimeout(timer) timer = null _self.apply(_me, args) }, interval || 500) } } window.resize = throttle(function(){ console.log(1); }, 1000)複製程式碼
分時函式
用於解決短時間內向內面插入大量的DOM節點,導致頁面渲染卡頓、假死的問題。
/** * @params ary 用於渲染dom節點的資料 * @parmas fn 渲染dom節點的業務邏輯 * @params count 一次渲染dom節點的數量 */ var timeChunk = function(ary, fn, count){ var obj, t; var start = function(){ for(var i = 0; i<Matn.min(count || 1, ary.length); i++){ var obj = ary.shift(); fn(obj) } } return function(){ t = setInterval(function(){ if(ary.length === 0){ return clearInterval(t) } start() }, 200) } }複製程式碼
惰性載入函式
在函式執行時,需要通過if語句判斷函式的執行分支,為提高效率,可以使用惰性載入函式