函數表達式和函數聲明式: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)
複製代碼
配置對象的優勢在於:
而缺點在於: