js模式第四章

第四章 函數

函數表達式和函數聲明式:node

//命名函數表達式
var demo=funtion d(){
    console.log("Hello World!");
};

//函數表達式
var demo=function(){
    console.log("Hello World!");
};

//函數聲明式
function demo(){
    console.log("Hello World!")
}
複製代碼

區別:

  • 函數表達式又分爲命令函數表達式函數表達式(匿名函數),而且函數表達式結尾須要加上分號,而函數聲明式則不須要。瀏覽器

  • 函數聲明式會進行變量提高而函數表達式則不會。緩存

    //全局函數 function foo(){ alert('global foo'); }安全

    function bar(){ alert('global bar') }閉包

    function hoistMe(){ console.log(typeof foo); //輸出 "function" console.log(typeof bar); //輸出 "undefined"app

    foo();//輸出"local foo"
      bar();//輸出TypeError: bar is not function
    
      //函數聲明
      //變量'foo'以及其實現着2被提高
      function foo(){
          alert('local foo');
      }
    
      //函數表達式
      //僅變量 'bar' 提高
      //函數實現並未提高
      var bar = function () {
          alert('local bar')
      }
    複製代碼

    }函數

回調模式

函數都是對象,函數能夠被當作參數傳遞給其餘函數,當一個函數被當作參數傳遞給另外一個函數,且執行,這叫回調。測試

Code:優化

function writeCode(callback){
    //do something
    callback();
}

function introduce(){
    // do something
}

//傳遞函數,執行回調
writeCode(introduce);
複製代碼

回調與做用域

若是傳遞的對象不是一個函數,而是一個對象的方法,而且這個方法用this來引用所屬的對象,那可能會致使意想不到的後果。this

假設一個方法paint(),它是一個名爲myapp的對象的方法:

var myapp={};
myapp.color="red";
myapp.paint=function(node){
    node.style.color=this.color;
};
複製代碼

函數findNodes()執行如下語句:

var findNodes=function(callback){
    // ...
    if(typeof callback === "function"){
        callback(found);
    }
};
複製代碼

若是執行findNodes(myapp.paint)並不會獲得如期的結果,由於此時this的引用的對象發生了改變,變成了findNodes,而findNodes並無color這個屬性。那咱們怎麼獲得預期的結果呢?

一個解決方法,咱們傳遞迴調函數,而且也傳遞迴調函數所屬的對象:

var findNodes = function(callback,callback_obj){
    // ...
    if(typeof callback === "function"){
        callback(callback_obj,found);
    }
};
複製代碼

那咱們的寫法將變成findNodes(myapp.paint,myapp),咱們也能夠將第一個參數寫成字符串的形式,這樣無需寫兩遍對象名, findNodes("paint",myapp),Code:

var findNodes = function(callback,callback_obj){
    // ...  
    if(typeof callback === "string" ){
        callback = callback_obj[callback]
    }

    if(typeof callback === "function"){
        callback.call(callback_obj,found)
    }
};
複製代碼

返回函數

函數也是對象,所以它們也能夠用做爲返回值。 下面有個demo:

var setup=function(){
    alert(1);
    return function(){
        alert(2);
    };
};
var my=setup(); //alert 1
my(); // alert 2
複製代碼

因爲setup()包裝了返回函數,它建立了一個閉包(粗俗的理解就是函數中的函數),能夠用這個閉包存儲一些私有數據,這些數據僅能夠被該返回函數訪問,但外部代碼卻沒法訪問。 Code:

var setup=function(){
    var count = 0;
    return function(){
        return (count+=1);
    };
};

var next=setup();
next(); //result is 1
next(); //result is 2
next(); //result is 3
複製代碼

自定義函數

若是建立了一個新函數而且將其分配給保存另外函數的同一個變量,那麼就以一個新函數覆蓋了舊函數。在某種程度上,回收了舊函數指針以指向一個新函數。而這一切發生在舊函數體的內部。在這種狀況下,該函數以一個新的實現覆蓋並從新定義了自身,這可能聽起來比實際上更復雜,Code:

var scareMe=function(){
    alert("Boo!");
    scareMe=function(){
        alert("Double boo!");
    };
};

scareMe(); //輸出 Boo
scareMe(); //輸出 Double boo!
複製代碼

當函數有且執行一次時,這種模式很是有效。 可是這種模式有個缺點,就是定義在自身時,已添加的屬性和方法都會丟失,此外,若是該函數使用了不一樣的名稱,好比從新分配給了另一個變量或者以對象的方法來使用,那麼重定義的部分將永遠不會發生。而且將會執行原函數體。 Code:

//1 添加一個新屬性
scareMe.property="property";

//2 賦值給另外一個不一樣名稱的變量
var prank=scareMe;

//3 做爲一個方法使用
var spooky = {
    boo:scareMe
};

// 
prank(); //輸出 "bool"
prank(); //輸出 "bool"
console.log(prank.property) //輸出 "property"

spooky.boo(); //輸出 "bool"
spooky.boo(); //輸出 "bool"
console.log(spooky.boo.property); //輸出 "property"

scareMe(); //輸出 "Double boo!"
scareMe(); //輸出 "Double boo!"
console.log(scareMe.property); // 輸出 "undefined"
複製代碼

當咱們以新名稱和做爲一個方法使用時,scareMe指針一直被重寫,至此scareMe函數自定義的部分一直沒有執行。直到當咱們直接執行scareMe時,指針沒有被重寫,自定義部分才得以執行。

即時函數

即時函數模式,該模式有一下幾部分組成:

  • 可使用函數表達式定義一個函數(函數聲明則沒法達到這個效果)
  • 在末尾添加一組括號,這將致使該函數當即執行。
  • 將整個函數包括在括號中(只有不將該函數分配給變量才須要這樣作)。

這種模式的好處就是造成了做用域沙箱。避免了全局污染,而且能夠執行一些一次新函數,而沒必要去建立複用函數。

(function(){
    var days=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
        today=new Date(),
        msg='Today is ' + days[today.getDay()] + ', ' + today.getDate();
    alert(msg);
}())
複製代碼

即時函數的參數

即時函數一樣能夠傳遞參數,Code:

;(function(who,when){
    console.log("I met " + who + " on " + when)
})("Nei",new Date());
複製代碼

即時函數的返回值

固然即時函數一樣能夠返回值,Code:

var result=(function(){
    return 2+2;
})();
複製代碼

賦值給一個變量時,即時函數能夠不須要最外層括號,Code:

var result=function(){
    return 2+2;
}();
複製代碼

即時函數不僅僅能夠返回整數值,一樣還能夠返回其餘類型,例如返回函數。造成一個閉包,在一個函數做用域中保留變量。Code:

var getResult=(function(){
    var res = 2 + 2;
    return function(){
        return res;
    };
})();
複製代碼

因而咱們能夠借用即時函數,在建立對象時,能夠將對象的屬性永久保留在內存中。Code:

var o = {
    message: (function(){
        var who = "me",
            what = "call";
        return who + " " + what;
    })(),
    getMsg:function(){
        return this.message;
    }
};
複製代碼

優勢

  • 即時函數模式建立了自調用的函數局部變量,避免了全局空間被臨時變量污染。

初始化時分支

初始化時分支時一種優化模式,當知道某個條件在整個程序生命週期內都不會發生改變的時候,僅對該條件測試一次時頗有意義的。瀏覽器嗅探(功能檢測)就是一個典型的例子。Code:

var utils = {
    addListener:function(el,type,fn){
        if(typeof window.addEvenListener === "function"){
            el.addEventListener(type,fn,false);
        }else if(typeof document.attachEvent === "function"){
            el.attachEven('on'+type,fn);
        }
    },
    removeListener:function(el,type,fn){
        //......
    }
};
// 當咱們每次調用utils.addListener 或 utils.removeListener時,都會重複執行檢測代碼。可是對於這樣不會變的特性時,咱們能夠執行一次性檢查代碼。
複製代碼

一次性檢查Code:

var utils = {
    addListener:null,
    removeListener:null
};

if(typeof window.addEventListener==="function"{
    utils.addListener = function(el,type,fn){
        el.addEventListener(type,fn,false);
    };
    utils.removeListener = function (el,type,fn) {
        el.removeEventListener(type,fn,false);
    }
}else if(){
    // ie......
}else{
    // 更早瀏覽器兼容模式
}
複製代碼

備忘模式

函數也是對象,咱們能夠添加屬性上去,例如咱們在大量計算的時候,就能夠將結果緩存在函數對象上,Code:

var myFunc = function (param){
    if(!myFunc.cache[param]){
        var result = {};
        //...大計算
        myFunc.cache[param]=result;
    }
    return myFunc.cache[param]
};
myFunc.cache={};
複製代碼

固然咱們的參數通常都不僅一個,這時怎麼辦呢?看Code:

var myFunc = function (){
    var cachekey = JSON.stringfy(Array.prototype.slice.call(arguments)),
        result;
    if(!myFunc.cache[cachekey]){
        result = {};
        //...大計算
        myFunc.cache[cachekey]=result;
    }
    return myFunc.cache[cachekey]
};
myFunc.cache={};
複製代碼

配置對象

調用函數,傳入參數時,一種傳參方式是多少個參數傳遞多少個參數,Code:

Demo(one,two,three,four,five,...);
複製代碼

可是這樣傳參,參數一旦不少,函數將變得難看和難以維護。 因此咱們傳參時,將參數寫進一個對象中,這樣傳參就是隻有傳遞一個對象。

var conf={
    username:'Nei',
    first:'N',
    last:'de'
};

Demo(conf)
複製代碼

配置對象的優勢在於:

  • 不須要記住衆多的參數以及其順序。
  • 能夠安全忽略可選參數。
  • 更加易於閱讀和維護。
  • 更加易於添加和刪除參數。

而缺點在於:

  • 須要記住參數名稱。
  • 屬性名稱沒法被壓縮。?
相關文章
相關標籤/搜索