前端總結·基礎篇·JS(三)arguments、callee、call、apply、bind及函數封裝和構造函數

前端總結系列

目錄

1、函數使用
    1.1 函數聲明和函數表達式
    1.2 函數封裝(自調用函數、閉包)
    1.3 函數屬性(arguments、callee)
    1.4 構造函數

2、函數技巧
    2.1 改變函數做用域(call/apply/bind)
    2.2 for循環中的setTimeout問題修復

1、函數使用

字符串和數組都是用來存儲數據。須要作一些特定的事情的時候,咱們就會會用函數封裝起來。javascript

1.1 函數聲明和函數表達式

聲明一個函數能夠用函數聲明或者函數表達式html

  • 函數表達式必須先聲明後調用
  • 函數內的變量須要使用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文件,爲了不彼此命名衝突。一般各自都會對內部函數進行封裝。(如今更好的方案是使用模塊化,模塊化之後再總結)前端

自調用函數java

使用自調用函數,把整個JS文件的代碼都封裝在內。git

  • 這樣作的好處是不會污染全局變量,能夠很好的減小命名衝突。
  • 最後把對外訪問的接口掛載在window上。
(function(){
    // some code ...
    window.myApp = myApp()  // 把對外訪問的接口掛載在window上
})()

閉包封裝es6

若是你內部使用的是函數表達式,而且不用var聲明,變量依舊會泄露到全局。github

// 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、函數技巧

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)
}

總結

全文主要參考如下網站

推薦的教程

全文主要是參考MDN寫出的總結。外加ChokCoco和JavaScript 祕密花園中的細緻分析,以及本身平時的一些總結寫成此文。

文中的錯誤還望可以指出,會及時作出修改(哪怕是錯別字)。Vue的基本理念如今差很少搞清楚了,大概在下週,會對Vue作一個總結(其實官方文檔是最好的總結 ^_^)。

相關文章
相關標籤/搜索