Javascript函數式編程要掌握的知識點講解

一:理解call和apply 及arguments.callee

ECMAScript3給Function的原型定義了兩個方法嘱么,他們是Function.prototype.callFunction.prototype.apply. 其實他們的作用是一樣的尺上,只是傳遞的參數不一樣而已陡厘;

1. apply; 接受2個參數,第一個參數指定了函數體內this對象的指向,第二個參數為一個類似數組的集合探入,比如如下代碼:

var yunxi = function(a,b){

   console.log([a,b]); // [1,2]

   console.log(this === window); // true

};

yunxi.apply(null,[1,2]);

如上代碼,我們第一個參數傳入null懂诗,函數體內默認會指向與宿主對象蜂嗽,即window對象;因此我們可以在yunxi函數內打印下值為true即可看到:

下面我們來看看使用call方法的實例如下:

var yunxi = function(a,b){

   console.log([a,b]); // [1,2]

   console.log(this === window); // true

};

yunxi.call(null,1,2);

可以看到 call方法的第二個參數是以逗號隔開的參數殃恒;

那么call和apply用在什么地方呢植旧?

1.call和apply 最常見的用途是改變函數體內的this指向,如下代碼:

var longen = {
    name:'yunxi'
};
var longen2 = {
    name: '我叫涂根華'
};
var name = "我是來測試的";
var getName = function(){
    return this.name;
};
console.log(getName()); // 打印 "我是來測試的";
console.log(getName.call(longen)); // 打印 yunxi
console.log(getName.call(longen2)); // 打印 "我叫涂根華"

第一次調用 getName()方法离唐,因為它是普通函數調用隆嗅,所以它的this指向與window,因此打印出全局對象的name的值侯繁;

第二次調用getName.call(longen);執(zhí)行這句代碼后胖喳,getName這個方法的內部指針this指向于longen這個對象了,因此打印this.name實際上是longen.name贮竟,因此返回的是name=”yunxi”;

但是this指針也有列外的情況丽焊,比如一個點擊元素,當我們點擊一個元素的時候咕别,this指針就指向與那個點擊元素技健,但是當我們在內部再包含一個函數后,在函數內再繼續(xù)調用this的話惰拱,那么現在的this指針就指向了window了雌贱;比如如下代碼:

document.getElementById("longen").onclick = function(){
    console.log(this); // this 就指向于div元素對象了
    var func = function(){
        console.log(this); // 打印出window對象
    }
    func();
}

如上代碼。可以看到外部this指向與被點擊的那個元素欣孤,內部普通函數調用馋没,this指針都是指向于window對象。但是我們可以使用call或者apply方法來改變this的指針的降传;如下代碼:

document.getElementById("longen").onclick = function(){
    console.log(this); // this 就指向于div元素對象了
    var func = function(){
        console.log(this); // 就指向于div元素對象了
    }
    func.call(this);
}

如上代碼我們使用call方法調用func函數篷朵,使this指向與func這個對象了痢站,當然上面的方法我們還可以不使用call或者apply方法來改變this的指針糖驴,我們可以在外部先使用一個變量來保存this的指針,在內部調用的時候我們可以使用哪個變量即可舰蟆,如下代碼演示:

document.getElementById("longen").onclick = function(){
    console.log(this); // this 就指向于div元素對象了
    var self = this;
    var func = function(){
        console.log(self); // 就指向于div元素對象了
    }
    func();
}  

arguments.callee的理解

callee是arguments的一個屬性段只,它可以被用作當前函數或函數體執(zhí)行的環(huán)境中腮猖,或者說調用一個匿名函數;返回的是當前正在被執(zhí)行的Function對象赞枕;簡單的來說就是當前執(zhí)行環(huán)境的函數被調用時候缚够,arguments.callee對象會指向與自身,就是當前的那個函數的引用鹦赎;

如下代碼:

var count = 1;
var test = function() {
    console.log(count + " -- " + (test.length == arguments.callee.length) );
    // 打印出 1 -- true 2 -- true  3 -- true 
    if (count++ < 3) {
        // 調用test()函數自身
        arguments.callee();
    }
};
test();

arguments.callee()的含義是調用當前正在執(zhí)行的函數自身谍椅,比如上面的test的匿名函數;

Function.prototype.bind介紹

目前很多高級瀏覽器都支持Function.prototype.bind方法古话,該方法用來指定函數內部的this指向雏吭。為了支持各個瀏覽器,我們也可以自己來簡單的模擬一個~

如下代碼:

Function.prototype.bind = function(context) {
    var self = this;
    return function(){
        return self.apply(context,arguments);
    }
}
var yunxi = {
    name: 'yunxi'
};

var func = function(){
    console.log(this.name); // yunxi
}.bind(yunxi);

func();

如上代碼所示:func這個函數使用調用bind這個方法陪踩,并且把對象yunxi作為參數傳進去杖们,然后bind函數使用return返回一個函數,當我們調用func()執(zhí)行這個方法的時候肩狂,其實我們就是在調用bind方法內的return返回的那個函數摘完,在返回的那個函數內context的上下文其實就是我們以參數yunxi對象傳進去的,因此this指針指向與yunxi這個對象了~ 所以打印出this.name 就是yunxi那個對象的name了;

除了上面我們看到的介紹apply或者call方法可以改變this指針外傻谁,我們還可以使用call或者apply來繼承對象的方法孝治;實質也就是改變this的指針了;

比如有如下代碼:

var Yunxi = function(name){
    this.name = name;
};

var Longen = function(){
    Yunxi.apply(this,arguments);
};

Longen.prototype.getName = function(){
    return this.name;
};

var longen = new Longen("tugenhua");
console.log(longen.getName());  // 打印出tugenhua

如上代碼:我先實例化Longen這個對象审磁,把參數傳進去谈飒,之后使用Yunxi.apply(this,arguments)這句代碼來改變Longen這個對象的this的指針,使他指向了Yunxi這個對象态蒂,因此Yunxi這個對象保存了longen這個實例化對象的參數tugenhua杭措,因此當我們調用longen.getName這個方法的時候,我們返回this.name钾恢,即我們可以認為返回的是Yunxi.name 因此返回的是 tugenhua手素,我們只是借用了下Yunxi這個對象內的this.name來保存Longen傳進去的參數而已鸳址;

二:閉包的理解

閉包的結構有如下2個特性

1.封閉性:外界無法訪問閉包內部的數據,如果在閉包內聲明變量泉懦,外界是無法訪問的稿黍,除非閉包主動向外界提供訪問接口;

2.持久性:一般的函數祠斧,調用完畢之后,系統(tǒng)自動注銷函數拱礁,而對于閉包來說琢锋,在外部函數被調用之后,閉包結構依然保存在

系統(tǒng)中呢灶,閉包中的數據依然存在吴超,從而實現對數據的持久使用。

缺點:

使用閉包會占有內存資源鸯乃,過多的使用閉包會導致內存溢出等.

如下代碼:

function a(x) {
    var a = x;
    var b = function(){
        return a;
    }
    return b;
}
var b = a(1);
console.log(b()); // 1

首先在a函數內定義了2個變量鲸阻,1個是存儲參數,另外一個是閉包結構缨睡,在閉包結構中保存著b函數內的a變量鸟悴,默認情況下,當a函數調用完之后a變量會自動銷毀的奖年,但是由于閉包的影響细诸,閉包中使用了外界的變量,因此a變量會一直保存在內存當中陋守,因此變量a參數沒有隨著a函數銷毀而被釋放震贵,因此引申出閉包的缺點是:過多的使用閉包會占有內存資源,或內存溢出等肯能性水评;

// 經典的閉包實列如下:
function f(x){              //外部函數
    var a = x;              // 外部函數的局部變量猩系,并傳遞參數
    var b = function(){     // 內部函數 
        return a;           // 訪問外部函數中的局部變量
    };
    a++;                    // 訪問后,動態(tài)更新外部函數的變量
    return b;               // 返回內部函數
}
var c = f(5);               // 調用外部函數并且賦值
console.log(c());           // 調用內部函數中燥,返回外部函數更新后的值為6

下面我們來看看如下使用閉包的列子

在如下代碼中有2個函數寇甸,f函數的功能是:把數組類型的參數中每個元素的值分別封裝在閉包結構中,然后把閉包存儲在一個數組中疗涉,并返回這個數組幽纷,但是在函數e中調用函數f并向其傳遞一個數組["a","b","c"],然后遍歷返回函數f返回數組,我們運行打印后發(fā)現都是c undefined博敬,那是因為在執(zhí)行f函數中的循環(huán)時候友浸,把值雖然保存在temp中,但是每次循環(huán)后temp值在不斷的變化偏窝,當for循環(huán)結束后收恢,此時temp值為c武学,同時i變?yōu)?,因此當調用的時候 打印出來的是temp為3伦意,arrs[3]變?yōu)閡ndefined火窒;因此打印出 c undefined

解決閉包的缺陷我們可以再在外面包一層函數,每次循環(huán)的時候驮肉,把temp參數和i參數傳遞進去 如代碼二

// 代碼一
function f(x) {
    var arrs = [];
    for(var i = 0; i < x.length; i++) {
        var temp = x[i];
        arrs.push(function(){
            console.log(temp + ' ' +x[i]); // c undefined
        });
    }
    return arrs;
}
function e(){
    var ar = f(["a","b","c"]);
    for(var i = 0,ilen = ar.length; i < ilen; i++) {
        ar[i]();
    }
}
e(); 
// 代碼二:
function f2(x) {
    var arrs = [];
    for(var i = 0; i < x.length; i++) {
        var temp = x[i];
        (function(temp,i){
            arrs.push(function(){
                console.log(temp + ' ' +x[i]); // c undefined
            });
        })(temp,i);
    }
    return arrs;
}
function e2(){
    var ar = f2(["a","b","c"]);
    for(var i = 0,ilen = ar.length; i < ilen; i++) {
        ar[i]();
    }
}
e2(); 

三:javascript中的this詳解

this的指向常見的有如下幾點需要常用到:

  1. 全局對象的this是指向與window熏矿;
  2. 作為普通函數調用。
  3. 作為對象方法調用离钝。
  4. 構造器調用票编。
  5. Function.prototype.callFunction.prototype.apply調用。

下面我們分別來介紹一下

1.全局對象的this卵渴;

console.log(this); // this指向于window
setTimeout() 和 setInterval()函數內部的this指針是指向于window的慧域,如下代碼:
 function test(){
    console.log(11);
 }
 setTimeout(function(){
    console.log(this === window); // true
    this.test(); // 11 
 });

2.作為普通函數調用;

如下代碼:

var name = "longen";
function test(){
    return this.name;
}
console.log(test()); // longen

當作為普通函數調用時候浪读,this總是指向了全局對象昔榴,在瀏覽器當中,全局對象一般指的是window碘橘;

3.作為對象的方法調用互订。

如下代碼:

var obj = {
    "name": "我的花名改為云溪了,就是為了好玩",
    getName: function(){
        console.log(this); // 在這里this指向于obj對象了
        console.log(this.name); // 打印 我的花名改為云溪了痘拆,就是為了好玩
    }
};
obj.getName(); // 對象方法調用

但是呢屁奏,我們不能像如下一樣調用對象了,如下調用對象的話错负,this還是執(zhí)行了window坟瓢,如下代碼:

var name = "全局對象名字";
var obj = {
    "name": "我的花名改為云溪了,就是為了好玩",
    getName: function(){
        console.log(this);  // window
        console.log(this.name); // 全局對象名字
    }
};
var yunxi = obj.getName; 
yunxi();

運行yunxi()函數犹撒,還是會像調用普通函數一樣折联,this指向了window的;

4.構造器調用识颊。
Javascript中不像Java一樣诚镰,有類的概念,而JS中只能通過構造器創(chuàng)建對象祥款,通過new 對象清笨,當new運算符調用函數時候,該函數會返回一個對象刃跛,一般情況下抠艾,構造器里面的this就是指向返回的這個對象;

如下代碼:

var Obj = function(){
    this.name = "yunxi";
};
var test = new Obj();
console.log(test.name); // yunxi

注意:構造器函數第一個字母需要大寫桨昙,這是為了區(qū)分普通函數還是構造器函數而言检号;

如上代碼:通過調用new Obj()方法 返回值保存到test變量中腌歉,那么test就是那個對象了,所以內部的this就指向與test對象了齐苛,因此test.name就引用到了內部的this.name 即輸出 “yunxi”字符串翘盖;

但是也有例外的情況,比如構造器顯示地返回了一個對象的話凹蜂,那么這次繼續(xù)調用的話馍驯,那么會最終會返回這個對象,比如如下代碼:

var obj = function(){
    this.name = "yunxi";
    return {
        "age": "27"
    }
};
var test = new obj();
console.log(test.name); // undefined

那么繼續(xù)調用的話玛痊,會返回unedfined汰瘫,因為返回的是那個對象,對象里面沒有name這個屬性卿啡,因此值為undefined吟吝;

四:理解函數引用和函數調用的區(qū)別

看下面的代碼分析:

// 函數引用 代碼一
function f(){
    var x = 5;
    return x;
}
var a = f;
var b = f;

console.log(a===b); // true
// 函數調用 代碼二
function f2() {
    var x = 5;
    return x;
}
var a2 = f2();
var b2 = f2();
console.log(a2 === b2);

// 函數調用 代碼三
function f3(){
    var x = 5;
    return function(){
        return x;
    }
}
var a3 = f3();
var b3 = f3();
console.log(a3 === b3); // false

如上的代碼:代碼一和代碼二分部是函數引用和函數調用的列子菱父,返回都為true颈娜,代碼三也是函數調用的列子,返回且為false

我們現在來理解下函數引用和函數調用的本質區(qū)別:當引用函數時候浙宜,多個變量內存存儲的是函數的相同的入口指針官辽,因此對于同一個函數來講,無論多少個變量引用粟瞬,他們都是相等的同仆,因為對于引用類型(對象,數組裙品,函數等)都是比較的是內存地址俗批,如果他們內存地址一樣的話,說明是相同的市怎;但是對于函數調用來講岁忘,比如代碼三;每次調用的時候,都被分配一個新的內存地址区匠,所以他們的內存地址不相同干像,因此他們會返回false,但是對于代碼二來講驰弄,我們看到他們沒有返回函數麻汰,只是返回數值,他們比較的不是內存地址戚篙,而是比較值五鲫,所以他們的值相等,因此他們也返回true岔擂,我們也可以看看如下實列化一個對象的列子臣镣,他們也被分配到不同的內存地址辅愿,因此他們也是返回false的;如下代碼測試:

function F(){
    this.x = 5;
}
var a = new F();
var b = new F();
console.log(a === b); // false

五:理解js中的鏈式調用

我們使用jquery的時候忆某,jquery的簡單的語法及可實現鏈式調用方法点待,現在我們自己也封裝一個鏈式調用的方法,來理解下 jquery中如何封裝鏈式調用 無非就是每次調用一個方法的時候 給這個方法返回this即可弃舒,this指向該對象自身癞埠,我們看看代碼:

// 定義一個簡單的對象,每次調用對象的方法的時候聋呢,該方法都返回該對象自身
var obj = {
    a: function(){
        console.log("輸出a");
        return this;
    },
    b:function(){
        console.log("輸出b");
        return this;
    }
};
console.log(obj.a().b()); // 輸出a 輸出b 輸出this指向與obj這個對象

// 下面我們再看下 上面的通過Function擴展類型添加方法的demo如下:
Function.prototype.method = function(name,func) {
    if(!this.prototype[name]) {
        this.prototype[name] = func;
        return this;
    }
}

String.method('trim',function(){
    return this.replace(/^\s+|\s+$/g,'');
});
String.method('log2',function(){
    console.log("鏈式調用");
    return this;
});
String.method('r',function(){
    return this.replace(/a/,'');
});
var str = " abc ";
console.log(str.trim().log2().r()); // 輸出鏈式調用和 bc

六:理解使用函數實現歷史記錄--提高性能

函數可以使用對象去記住先前操作的結果苗踪,從而避免多余的運算。比如我們現在測試一個費波納茨的算法削锰,我們可以使用遞歸函數計算fibonacci數列通铲,一個fibonacci數字是之前兩個fibonacci數字之和,最前面的兩個數字是0和1器贩;代碼如下:

var count = 0;
 var fibonacci = function(n) {
    count++;
    return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
 };
 for(var i = 0; i <= 10; i+=1) {
    console.log(i+":"+fibonacci(i));
 }
 console.log(count); // 453

我們可以看到如上 fibonacci函數總共調用了453次颅夺,for循環(huán)了11次,它自身調用了442次蛹稍,如果我們使用下面的記憶函數的話吧黄,那么就可以減少他們的運算次數,從而提高性能唆姐;

思路:先使用一個臨時數組保存存儲結果拗慨,當函數被調用的時候,先看是否已經有存儲結果 如果有的話奉芦,就立即返回這個存儲結果赵抢,否則的話,調用函數運算下声功;代碼如下:

var count2 = 0;
var fibonacci2 = (function(){
    var memo = [0,1];
    var fib = function(n) {
        var result = memo[n];
        count2++;
        if(typeof result !== 'number') {
            result = fib(n-1) + fib(n-2);
            memo[n] = result;
        }
        return result;
    };
    return fib;
 })();
 for(var j = 0; j <= 10; j+=1) {
    console.log(j+":"+fibonacci2(j));
 }
 console.log(count2); // 29

這個函數也返回了同樣的結果烦却,但是只調用了函數29次,循環(huán)了11次减噪,也就是說函數自身調用了18次短绸,從而減少無謂的函數的調用及運算,下面我們可以把這個函數進行抽象化筹裕,以構造帶記憶功能的函數醋闭,如下代碼:

var count3 = 0;
var memoizer = function(memo,formula) {
    var recur = function(n) {
        var result = memo[n];
        count3++;   // 這句代碼只是說明運行函數多少次,在代碼中并無作用朝卒,實際使用上可以刪掉
        if(typeof result !== 'number') {
            result = formula(recur,n);
            memo[n] = result;
        }
        return result;
    };
    return recur;
 };
 var fibonacci3 = memoizer([0,1],function(recur,n){
    return recur(n-1) + recur(n-2);
 });
 // 調用方式如下
 for(var k = 0; k <=10; k+=1) {
    console.log(k+":"+fibonacci3(k));
 }
 console.log(count3); // 29

如上封裝 memoizer 里面的參數是實現某個方法的計算公式证逻,具體的可以根據需要自己手動更改,這邊的思路無非就是想習慣使用對象去保存臨時值抗斤,從而減少不必要的取值存儲值的操作囚企;

七:理解通過Function擴展類型

javascript 允許為語言的基本數據類型定義方法丈咐。通過Object.prototype添加原型方法,該方法可被所有的對象使用龙宏。

這對函數棵逊,字符串,數字银酗,正則和布爾值都適用辆影,比如如下現在給Function.prototype增加方法,使該方法對所有函數都可用黍特,代碼如下:

Function.prototype.method = function(name,func) {
    if(!this.prototype[name]) {
        this.prototype[name] = func;
        return this;
    }
}
Number.method('integer',function(){
    return Math[this < 0 ? 'ceil' : 'floor'](this);
});
console.log((-10/3).integer()); // -3

String.method('trim',function(){
    return this.replace(/^\s+|\s+$/g,'');
});
console.log(" abc ".trim()); // abc

八:理解使用模塊模式編寫代碼

使用函數和閉包可以構建模塊蛙讥,所謂模塊,就是一個提供接口卻隱藏狀態(tài)與實現的函數或對象灭衷。使用函數構建模塊的優(yōu)點是:減少全局變量的使用次慢;

比如如下:我想為String擴展一個方法,該方法的作用是尋找字符串中的HTML字符字體并將其替換為對應的字符翔曲;

// 如下代碼:
Function.prototype.method = function(name,func) {
    if(!this.prototype[name]) {
        this.prototype[name] = func;
        return this;
    }
}
String.method('deentityify',function(){
    var entity = {
        quot: '"',
        It: '<',
        gt: '>'
    };
    return function(){
        return this.replace(/&([^&;]+);/g,function(a,b){
            var r = entity[b];
            return typeof r === 'string' ? r : a;
        });
    }
}());
console.log("&It;">".deentityify()); // <">

模塊模式利用函數作用域和閉包來創(chuàng)建綁定對象與私有成員的關聯迫像,比如在上面的deentityify()方法才有權訪問字符實體表entity這個數據對象;

模塊開發(fā)的一般形式是:定義了私有變量和函數的函數部默,利用閉包創(chuàng)建可以訪問到的私有變量和函數的特權函數侵蒙,最后返回這個特權函數造虎,或把他們保存到可以訪問的地方傅蹂。

模塊模式一般會結合實例模式使用。javascript的實例就是使用對象字面量表示法創(chuàng)建的算凿。對象的屬性值可以是數值或者函數份蝴,并且屬性值在該對象的生命周期中不會發(fā)生變化;比如如下代碼屬于模塊模式:定義了一個私有變量name屬性氓轰,和一個實例模式(對象字面量obj)并且返回這個對象字面量obj婚夫,對象字面量中的方法與私有變量name進行了綁定;

// 比如如下經典的模塊模式
var MODULE = (function(){
    var name = "tugenhua";
    var obj = {
        setName: function() {
            this.name = name;
        },
        getName: function(){
            return this.name;
        }
    };
    return obj;
})();
MODULE.setName()
console.log(MODULE.getName()); // tugenhua

九:理解惰性實列化

在頁面中javascript初始化執(zhí)行的時候就實例化類署鸡,如果在頁面中沒有使用這個實列化的對象案糙,就會造成一定的內存浪費和性能損耗;這時候靴庆,我們可以使用惰性實列化來解決這個問題时捌,惰性就是把實列化推遲到需要使用它的時候才去做,做到 "按需供應";

// 惰性實列化代碼如下
var myNamespace = function(){
    var Configure = function(){
        var privateName = "tugenhua";
        var privateGetName = function(){
            return privateName;
        };
        var privateSetName = function(name) {
            privateName = name;
        };
        // 返回單列對象
        return {
            setName: function(name) {
                privateSetName(name);
            },
            getName: function(){
                return privateGetName();
            }
        }
    };
    // 存儲Configure實列
    var instance;
    return {
        init: function(){
            // 如果不存在實列炉抒,就創(chuàng)建單列實列
            if(!instance) {
                instance = Configure();
            }
            // 創(chuàng)建Configure單列
            for(var key in instance) {
                if(instance.hasOwnProperty(key)) {
                    this[key] = instance[key];
                }
            }
            this.init = null;
            return this;
        }
    }
}();
// 調用方式
myNamespace.init();
var name = myNamespace.getName();
console.log(name); // tugenhua

如上代碼是惰性化實列代碼:它包括一個單體Configure實列奢讨,直接返回init函數,先判斷該單體是否被實列化焰薄,如果沒有被實列化的話拿诸,則創(chuàng)建并執(zhí)行實列化并返回該實列化扒袖,如果已經實列化了,則返回現有實列亩码;執(zhí)行完后季率,則銷毀init方法,只初始化一次

十:推薦分支函數(解決兼容問題的更好的方法)

分支函數的作用是:可以解決兼容問題if或者else的重復判斷的問題描沟,我們一般的做法是:根據兼容的不同寫if蚀同,else等,這些判斷來實現兼容啊掏,但是這樣明顯就有一個缺點蠢络,每次執(zhí)行這個函數的時候,都需要進行if和else的檢測迟蜜,效率明顯不高刹孔,我們現在使用分支函數來實現當初始化的時候進行一些檢測,在之后的運行代碼過程中娜睛,代碼就無需檢測了髓霞;

// 我們先來看看傳統(tǒng)的封裝ajax請求的函數
//創(chuàng)建XMLHttpRequest對象:
var xmlhttp;
function createxmlhttp(){
    if (window.XMLHttpRequest){
        // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else{
      // code for IE6, IE5
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
}
// 下面我們看看分支函數代碼如下:
var XHR = (function(){
    var standard = {
        createXHR : function() {
            return new XMLHttpRequest();
        }
    };
    var oldActionXObject = {
        createXHR : function(){
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    };
    var newActionXObject = {
        createXHR : function(){
            return new ActiveXObject("Msxml2.XMLHTTP");
        }
    };
    if(standard.createXHR) {
        return standard;
    }else {
        try{
            newActionXObject.createXHR();
            return newActionXObject;
        }catch(e){
            oldActionXObject.createXHR();
            return oldActionXObject;
        }
    }
})();
console.log(XHR.createXHR()); //xmlHttpRequest對象

上面的代碼就是分支函數,分支的原理是:聲明幾個不同名稱的對象畦戒,且為該不同名稱對象聲明一個相同的方法方库,然后根據不同的瀏覽器設計來實現,接著開始進行瀏覽器檢測障斋,并且根據瀏覽器檢測來返回哪一個對象纵潦,不論返回的是哪一個對象,最后它一致對外的接口都是createXHR方法的垃环;

十一:惰性載入函數(也是解決兼容問題的)

和上面分支的原理是一樣的邀层,代碼也可以按照上面的推薦分支風格編碼的;解決的問題也是解決多個if條件判斷的遂庄;代碼如下:

// 代碼如下:
var addEvent = function(el,type,handler){
    addEvent = el.addEventListener ? function(el,type,handler){
        el.addEventListener(type,handler,false);
    } : function(el,type,handler) {
        el.attachEvent("on" + type,handler);
    }
    addEvent(el,type,handler);
};

惰性載入函數也是在函數內部改變自身的一種方式寥院,在重復執(zhí)行的時候就不會再進行檢測的;惰性載入函數的分支只會執(zhí)行一次涛目,即第一次調用的時候秸谢,其優(yōu)點如下:

  1. 要執(zhí)行的適當代碼只有在實際調用函數時才執(zhí)行。

  2. 第一次調用該函數的時候霹肝,緊接著內部函數也會執(zhí)行估蹄,但是正因為這個,所以后續(xù)繼續(xù)調用該函數的話阿迈,后續(xù)的調用速度會很快元媚;因此避免了多重條件;

十二:理解函數節(jié)流

DOM操作的交互需要更多的內存和CPU時間,連續(xù)進行過多的DOM相關的操作可能會導致瀏覽器變慢甚至崩潰刊棕,函數節(jié)流的設計思想是讓某些代碼可以在間斷的情況下連續(xù)重復執(zhí)行炭晒,實現該方法可以使用定時器對該函數進行節(jié)流操作;

比如:第一次調用函數的時候,創(chuàng)建一個定時器甥角,在指定的時間間隔下執(zhí)行代碼网严。當第二次執(zhí)行的時候,清除前一次的定時器并設置另一個嗤无,將其替換成一個新的定時器;

// 如下簡單函數節(jié)流代碼演示
var throttle = {
    timeoutId: null,
    // 需要執(zhí)行的方法
    preformMethod: function(){

    },
    // 初始化需要調用的方法
    process: function(){
        clearTimeout(this.timeoutId);
        var self = this;
        self.timeoutId = setTimeout(function(){
            self.preformMethod();
        },100);
    }
};
// 執(zhí)行操作
throttle.process();

函數節(jié)流解決的問題是一些代碼(比如事件)無間斷的執(zhí)行震束,這可能會影響瀏覽器的性能,比如瀏覽器變慢或者直接崩潰当犯。比如對于mouseover事件或者click事件垢村,比如點擊tab項菜單,無限的點擊嚎卫,有可能會導致瀏覽器會變慢操作嘉栓,這時候我們可以使用函數節(jié)流的操作來解決;

作者:涂根華
原文地址:http://www.cnblogs.com/tugenhua0707/p/5046854.html

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末拓诸,一起剝皮案震驚了整個濱河市侵佃,隨后出現的幾起案子,更是在濱河造成了極大的恐慌奠支,老刑警劉巖馋辈,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異倍谜,居然都是意外死亡迈螟,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門枢劝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來井联,“玉大人卜壕,你說我怎么就攤上這事您旁。” “怎么了轴捎?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵鹤盒,是天一觀的道長。 經常有香客問我侦副,道長侦锯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任秦驯,我火速辦了婚禮尺碰,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己亲桥,他們只是感情好洛心,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著题篷,像睡著了一般词身。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上番枚,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天法严,我揣著相機與錄音,去河邊找鬼葫笼。 笑死深啤,一個胖子當著我的面吹牛,可吹牛的內容都是我干的路星。 我是一名探鬼主播墓塌,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奥额!你這毒婦竟也來了苫幢?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤垫挨,失蹤者是張志新(化名)和其女友劉穎韩肝,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體九榔,經...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡哀峻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了哲泊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剩蟀。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖切威,靈堂內的尸體忽然破棺而出育特,到底是詐尸還是另有隱情,我是刑警寧澤先朦,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布缰冤,位于F島的核電站,受9級特大地震影響喳魏,放射性物質發(fā)生泄漏棉浸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一刺彩、第九天 我趴在偏房一處隱蔽的房頂上張望迷郑。 院中可真熱鬧枝恋,春花似錦、人聲如沸嗡害。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽就漾。三九已至呐能,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抑堡,已是汗流浹背摆出。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留首妖,地道東北人偎漫。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像有缆,于是被迫代替她去往敵國和親象踊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內容