前端總結系列
- 前端總結·基礎篇·CSS(一)布局
- 前端總結·基礎篇·CSS(二)視覺
- 前端總結·基礎篇·CSS(三)補充
- 前端總結·基礎篇·JS(一)原型、原型鏈、構造函數和字符串(String)
- 前端總結·基礎篇·JS(二)數組深拷貝、去重以及字符串反序和數組(Array)
- 前端總結·基礎篇·JS(三)arguments、callee、call、apply、bind及函數封裝和構造函數
目錄
一、函數使用
1.1 函數聲明和函數表達式
1.2 函數封裝(自調用函數、閉包)
1.3 函數屬性(arguments、callee)
1.4 構造函數
二、函數技巧
2.1 改變函數作用域(call/apply/bind) 2.2 for循環中的setTimeout問題修復
一、函數使用
字符串和數組都是用來存儲數據。需要做一些特定的事情的時候,我們就會會用函數封裝起來。
1.1 函數聲明和函數表達式
聲明一個函數可以用函數聲明或者函數表達式。
- 函數表達式必須先聲明后調用
- 函數內的變量需要使用var聲明,否則會污染全局變量
function hello( name ) { some code ... } // 函數聲明 var hello = function ( name ) { some code ... } // 函數表達式 // 變量提升 | 函數內部的變量未使用var聲明,執行后會導致變量成為全局變量 var age = 22 function showAge(){ age = 23 console.log(age) } showAge() // 23 | 執行函數 console.log(age) // 23 | 測試全局變量age的值
1.2 函數封裝(自調用函數、閉包)
實際項目中會引入很多外部JS文件,為了避免彼此命名沖突。通常各自都會對內部函數進行封裝。(現在更好的方案是使用模塊化,模塊化以后再總結)
自調用函數
使用自調用函數,把整個JS文件的代碼都封裝在內。
- 這樣做的好處是不會污染全局變量,可以很好的減少命名沖突。
- 最后把對外訪問的接口掛載在window上。
(function(){ // some code ... window.myApp = myApp() // 把對外訪問的接口掛載在window上 })()
閉包封裝
如果你內部使用的是函數表達式,並且不用var聲明,變量依舊會泄露到全局。
// a.js (function(){ function myApp() { // 定義showName方法 function showName(name) { console.log(name) } // 定義showAge方法 function showAge(age){ console.log(age) } // 返回一個對象 return { showName: showName, showAge: showAge } } window.myApp = myApp() })()
功能擴展
引入上面的a.js文件,並且創建b.js來寫程序主邏輯。
- 你也可以寫在一個文件,寫在兩個文件是為了把接口和控制器分開,方便管理。
- 你可以修改上面提供的方法,或者新添加一條方法。
//b.js // 修改已有方法 myApp.showName = function (name) { console.log('Call me '+name) } // 定義新方法 myApp.showCity = function (city) { console.log(city) } myApp.showName('berg') // 能夠訪問 | berg myApp.showCity('NanChang') // 能夠訪問 | NanChang showName('berg') // 不能訪問 | Uncaught ReferenceError: showName is not defined
1.3 函數屬性(arguments、callee)
arguments是用來存放實參的,可以通過下標訪問實參的值。callee指向當前執行的函數,可以在遞歸的時候用。具體遞歸場景以及代碼,請見MDN。
- callee不能實現尾遞歸
- callee在ES5嚴格模式中禁止使用
- caller指向調用當前函數的函數(已廢棄)
function show(name,age) { console.log(name) // berg console.log(age) // 22 console.log(arguments) // ["berg", 22] console.log(arguments.callee) // function show(name,age) {} } show('berg',22)
1.4 構造函數
ES6可以使用Class實現繼承,以后在單獨總結ES6的時候會提到。推薦一個非常好的ES6系列教程,深入淺出ES6
// 聲明構造函數 Human function Human() { this.play = function(){ console.log('I\'m playing.') } } // 聲明構造函數 Male function Male() { this.sex = 'male' } // 聲明構造函數 Female function Female() { this.sex = 'female' } // 讓Male和Female繼承Human Male.prototype = new Human() Female.prototype = new Human() // 創建xiaoming對象並且測試繼承結果 var xiaoming = new Male() console.log(xiaoming.sex) // male console.log(xiaoming.play()) // I'm playing. // 創建xiaohong對象並且測試繼承結果 var xiaohong = new Female() console.log(xiaohong.sex) // female console.log(xiaohong.play()) // I'm playing.
二、函數技巧
2.1 改變函數作用域(call/apply/bind)
當需要改變上下文的this的時候,可以使用call/apply/bind。(更詳細的解釋請見ChokCoco,需要用call實現繼承請見MDN)
- 三種方法的第一個參數都是this的上下文
- apply第二個參數是數組,call和bind后面都是接單個參數
- call和apply默認會自動執行,bind需要在后面加()來自動執行
- bind是ES5語法,支持IE9+
/* 以下以數組合並為例子 * 使用不同的方法之前,請確保a和b為默認值 */ var a = [1,2,3] // 測試用的默認值 var b = [4,5,6] // 測試用的默認值 a.push(b) // 直接使用push(不符合預期) console.log(a) // [1, 2, 3, Array[3]] // call方法(在這種情況下要逐個輸入參數,不太方便) Array.prototype.push.call(a,4,5,6) console.log(a) // [1, 2, 3, 4, 5, 6] // apply方法(第二個參數直接傳入數組,非常適用這種場景) Array.prototype.push.apply(a,b) console.log(a) // [1, 2, 3, 4, 5, 6] // bind方法 Array.prototype.push.bind(a,4,5,6)() // 注意看,這里需要加一個自動執行函數 console.log(a) // [1, 2, 3, 4, 5, 6]
2.2 for循環中的setTimeout問題修復
setTimeout有自己的this。如果在外層放一個for循環,意味着會一次性執行完,而沒有起到延時的作用。解決方案是使用閉包。此處主要參考JavaScript 秘密花園
- setTimeout是一個定時執行函數。接受兩個參數,第一個是執行的函數,第二個是延遲執行的時間。通常用在登陸之后,提示幾秒鍾之后跳轉到首頁(現在基本不這樣做了)。
- setInterval和setTimeout基本一致。只是第二個時間參數,表示的是每多長事件執行一次。
- 第二個參數的時間單位是毫秒(1000表示為1秒)
// 使用閉包前 for(var x = 0; x<10; x++) { setTimeout(run,1000) // 10 } function run() { console.log(x) } // 使用閉包后 for(var x = 0; x<10; x++) { setTimeout((function (x) { return function() { console.log(x) // 0 1 2 3 4 5 6 7 8 9 } })(x),1000) }
總結
全文主要參考以下網站
推薦的教程
- JavaScript系列 @ 廖雪峰
- JavaScript 標准參考教程(alpha) @ 阮一峰
- 深入理解JavaScript @ 湯姆大叔
- 深入淺出ES6
- ECMAScript 6 入門 @ 阮一峰
- 張鑫旭博客
全文主要是參考MDN寫出的總結。外加ChokCoco和JavaScript 秘密花園中的細致分析,以及自己平時的一些總結寫成此文。
文中的錯誤還望能夠指出,會及時做出修改(哪怕是錯別字)。Vue的基本理念現在差不多搞清楚了,大概在下周,會對Vue做一個總結(其實官方文檔是最好的總結 ^_^)。
