JavaScript模式 讀書筆記三

第四章函數

函數是第一類對象 first-class object,能夠做爲帶有屬性和方法的值以及參數進行傳遞。 node

建立函數的語法 瀏覽器

function foo(){}; //函數聲明
var bar=function(){}; //函數表達式
var baz=function baz(){};//函數命名錶達式



函數的提高


//反模式
//僅用於掩飾

//全局函數
function foo(){
  alert("global foo");
}
function bar(){
  alert("global bar");
}

function hoisMe(){
  console.log(typeof foo); //輸出function
  console.log(typeof bar); //輸出undefined
  foo();  //輸出local foo
  bar();  //輸出 TypeError: bar is not a function

  //函數聲明
  //變量 foo以其實現者被提高
  function foo(){
    alert("local foo");
  }

  //函數表達式
  //僅變量 bar被提高
  //函數實現並未被提高
  var bar=function(){
     alert("local bar");
  }


}

hoisMe();


回調示例


//重構findNodes()以接受一個回調函數
var findNodes=function(callback){
  var i=100000,
  nodes=[],
  found;

  if(typeof callback!=="function"){
    callback=false;
  }

  while(i){
    i-=1;
    // 這裏是複雜的邏輯...

    //如今進行回調函數
    if(callback){
      callback(found);
    }

    nodes.push(found);

  }
  return nodes;
}

//回調函數
var hide=function(node){
  node.style.display="none";

}
//找到制定節點,並在後續執行中將其隱藏起來
findNodes(hide);



//傳遞一個匿名回調函數
findNodes(function(node){
  node.style.display="block";
});


回調與做用域



//重構findNodes()以接受一個回調函數
var findNodes=function(callback,callback_obj){
  var i=100000,
  nodes=[],
  found;



  while(i){
    i-=1;
    // 這裏是複雜的邏輯...

    //如今進行回調函數
    if(typeof callback==="string"){
      callback=callback_obj[callback];
    }
    if(typeof callback==="function"){
       callback.call(callback_obj,found);
    }

    nodes.push(found);

  }
  return nodes;
}

//回調函數
var myapp={};
myapp.color="green";
myapp.paint=function(node){
  node.style.color=this.color;
};
//找到制定節點,並在後續執行中將其隱藏起來
findNodes(myapp.paint,myapp);


返回函數


var setup=function(){
  alert(1);
  return function(){
    alert(2);
  }
}

//使用setup函數
var my=setup(); //alert1
my(); //alert 2



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

//用法
var next=setup();
next(); //返回1
next(); //返回2
next(); //返回3


自定義函數

惰性函數定義 lazy function definition 緩存

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

//使用自定義函數
scareMe(); //輸出Boo!
scareMe(); //輸出Double boo!



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

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

//2. 複製給另外一個不一樣名稱的變量
var prank=scareMe;

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

//calling with a new name
prank(); //輸出 Boo!
prank(); //輸出 Boo!
 console.log(prank.property); //輸出properly

 //做爲一個方法來調用
 spooky.boo(); //輸出Boo!
 spooky.boo(); //輸出Boo!
 console.log(spooky.boo.property); // 輸出  properly

 //使用自定義函數
 scareMe(); //輸出 Double boo!
 scareMe(); //輸出 Double boo!
console.log(scareMe.property); //undefined


即時函數的返回值

即時函數也叫自調用self-invoking 或者自執行self-executing函數。 閉包

即時函數中定義的變量將會用於它自身的局部變量,沒必要擔憂全局空間被臨時的變量污染。 app

//定義模塊module1
(function(){
  //模塊1中的全部代碼
}());



本例子中即時函數的返回值是一個函數,它將分配給變量getResult,而且將簡單地返回res值,該值被預計算並存儲在即時函數的閉包中 ide

var getResult=(function(){
  var res=2+2;
  return function(){
    return res;
  }
}());


定義對象屬性時也可使用即時函數 函數

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

//用法
console.log(o.getMsg());  //輸出call me
console.log(o.message);   //輸出call me


即時對象初始化

immediate object initalization 測試

({
  //在這裏能夠定義設定值
  //又名配置常數
  maxwidth:600,
  maxheight:400,

  //還能夠定義一些實用的方法
  gimmeMax:function(){
    return this.maxwidth+ "X" +this.maxheight;
  },

  //初始化
  init:function(){
    console.log(this.gimmeMax());
    //更多初始化任務。。。
  }

}).init();


初始化時分支

init-time branching 也稱爲加載時分支 load-time branching 是一種優化模式 優化

//接口
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(typeof document.attachEvent==="function"){  //判斷爲IE瀏覽器
  utils.addListener=function(el,type,fn){
    el.attachEvent('on'+type,fn);
  };
  utils.removeListener=function(el,type,fn){
    el.detachEvent("on"+type,fn);
  };

}else{  //更早版本的瀏覽器 
   utils.addListener=function(el,type,fn){
     el['on'+type]=fn;
   };
   utils.removeListener=function(el,type,fn){
     el['on'+type]=null;
   }
}



函數屬性——備忘模式

對於每一個函數,它都會自動得到一個length屬性,其中包含了該函數指望的參數數量。 this

function func(a,b,c,d){}
console.log(func.length);


能夠在任什麼時候候將自定義屬性添加到你的函數中。自定義屬性的其中一個用例是緩存函數結果(即返回值),所以下次調用該函數時就不會重作潛在的繁重計算。 緩存函數結果也被稱爲備忘memoization 


var myFunc=function(param){
  if(!myFunc.cache[param]){
    var result={};
    //。。。開銷很大的操做。。。
    myFunc.cache[param]=result;
  }
  return myFunc.cache[param];
}
//緩存存儲
myFunc.cache={};


配置對象

當須要傳遞大量參數時,一個更好的辦法是僅使用一個參數對象來代替全部參數,該參數對象稱爲conf,也就是配置的意思。


var conf={
  username:"lilu",
  first:"Yi",
  last:"Liu"
}

function addPerson(person){
  return person.username +" " +person.first+" "+person.last;
}

addPerson(conf);


Curry


函數應用

調用invoking函數和應用applying函數能夠獲得相同的結果

//定義函數
var sayHi=function(who){
  return "Hello"+ (who?", "+who:"")+"!";
};

//調用函數
sayHi(); //輸出Hello
sayHi('world'); //輸出Hello,World!

//應用函數
sayHi.apply(null,['hello']); //輸出Hello, hello!



Curry化


//curry化的add()函數
//接受部分參數的列表
function add(x,y){
  var oldx=x,oldy=y;
  if(typeof oldy==="undefined"){ //部分
    return function(newy){
      return oldx+newy;
    }
  }
  //徹底應用
  return x+y;
}
//測試
typeof add(5); //輸出function
add(3)(4); //7
//建立病存儲一個新函數
var add2000=add(2000);
add2000(10);//輸出2010

一個通用curry化函數


function schonfinkelize(fn){
  var slice=Array.prototype.slice,
    stored_args=slice.call(arguments,1);
  return function(){
    var new_args=slice.call(arguments),
        args=stored_args.concat(new_args);
    return fn.apply(null,args);
  }
}

//普通函數
function add(x,y){
  return x+y;
}

//將一個函數curry化以得到一個新的函數
var newadd=schonfinkelize(add,5);
newadd(4); //輸出9

/另外一種選擇——直接調用新函數
schonfinkelize(add,6)(7);


更多用法


//普通函數
function add(a,b,c,d,e){
  return a+b+c+d+e;
}

//可運行於任意數量的參數
schonfinkelize(add,1,2,3)(5,5); //16

//兩步curry化
var addOne=schonfinkelize(add,1);
addOne(10,10,10,10); //輸出41

var addSix=schonfinkelize(addOne,2,3);
addSix(5,5); //輸出16
相關文章
相關標籤/搜索