Javascript函數(shù)式編程要掌握的知識點(diǎn)講解

一:理解call和apply 及arguments.callee

ECMAScript3給Function的原型定義了兩個方法阔加,他們是Function.prototype.callFunction.prototype.apply. 其實(shí)他們的作用是一樣的捞魁,只是傳遞的參數(shù)不一樣而已孙乖;

1. apply; 接受2個參數(shù)统倒,第一個參數(shù)指定了函數(shù)體內(nèi)this對象的指向佃声,第二個參數(shù)為一個類似數(shù)組的集合,比如如下代碼:

var yunxi = function(a,b){

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

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

};

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

如上代碼几莽,我們第一個參數(shù)傳入null神郊,函數(shù)體內(nèi)默認(rèn)會指向與宿主對象肴裙,即window對象;因此我們可以在yunxi函數(shù)內(nèi)打印下值為true即可看到:

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

var yunxi = function(a,b){

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

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

};

yunxi.call(null,1,2);

可以看到 call方法的第二個參數(shù)是以逗號隔開的參數(shù)涌乳;

那么call和apply用在什么地方呢蜻懦?

1.call和apply 最常見的用途是改變函數(shù)體內(nèi)的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)); // 打印 "我叫涂根華"

第一次調(diào)用 getName()方法夕晓,因?yàn)樗瞧胀ê瘮?shù)調(diào)用宛乃,所以它的this指向與window,因此打印出全局對象的name的值蒸辆;

第二次調(diào)用getName.call(longen);執(zhí)行這句代碼后征炼,getName這個方法的內(nèi)部指針this指向于longen這個對象了,因此打印this.name實(shí)際上是longen.name躬贡,因此返回的是name=”yunxi”;

但是this指針也有列外的情況谆奥,比如一個點(diǎn)擊元素,當(dāng)我們點(diǎn)擊一個元素的時候拂玻,this指針就指向與那個點(diǎn)擊元素酸些,但是當(dāng)我們在內(nèi)部再包含一個函數(shù)后宰译,在函數(shù)內(nèi)再繼續(xù)調(diào)用this的話,那么現(xiàn)在的this指針就指向了window了魄懂;比如如下代碼:

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

如上代碼沿侈。可以看到外部this指向與被點(diǎn)擊的那個元素市栗,內(nèi)部普通函數(shù)調(diào)用缀拭,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方法調(diào)用func函數(shù)蛛淋,使this指向與func這個對象了,當(dāng)然上面的方法我們還可以不使用call或者apply方法來改變this的指針盲赊,我們可以在外部先使用一個變量來保存this的指針铣鹏,在內(nèi)部調(diào)用的時候我們可以使用哪個變量即可,如下代碼演示:

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的一個屬性哀蘑,它可以被用作當(dāng)前函數(shù)或函數(shù)體執(zhí)行的環(huán)境中诚卸,或者說調(diào)用一個匿名函數(shù);返回的是當(dāng)前正在被執(zhí)行的Function對象绘迁;簡單的來說就是當(dāng)前執(zhí)行環(huán)境的函數(shù)被調(diào)用時候合溺,arguments.callee對象會指向與自身,就是當(dāng)前的那個函數(shù)的引用缀台;

如下代碼:

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

arguments.callee()的含義是調(diào)用當(dāng)前正在執(zhí)行的函數(shù)自身棠赛,比如上面的test的匿名函數(shù);

Function.prototype.bind介紹

目前很多高級瀏覽器都支持Function.prototype.bind方法膛腐,該方法用來指定函數(shù)內(nèi)部的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這個函數(shù)使用調(diào)用bind這個方法哲身,并且把對象yunxi作為參數(shù)傳進(jìn)去辩涝,然后bind函數(shù)使用return返回一個函數(shù),當(dāng)我們調(diào)用func()執(zhí)行這個方法的時候勘天,其實(shí)我們就是在調(diào)用bind方法內(nèi)的return返回的那個函數(shù)怔揩,在返回的那個函數(shù)內(nèi)context的上下文其實(shí)就是我們以參數(shù)yunxi對象傳進(jìn)去的,因此this指針指向與yunxi這個對象了~ 所以打印出this.name 就是yunxi那個對象的name了;

除了上面我們看到的介紹apply或者call方法可以改變this指針外脯丝,我們還可以使用call或者apply來繼承對象的方法商膊;實(shí)質(zhì)也就是改變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

如上代碼:我先實(shí)例化Longen這個對象宠进,把參數(shù)傳進(jìn)去晕拆,之后使用Yunxi.apply(this,arguments)這句代碼來改變Longen這個對象的this的指針,使他指向了Yunxi這個對象材蹬,因此Yunxi這個對象保存了longen這個實(shí)例化對象的參數(shù)tugenhua实幕,因此當(dāng)我們調(diào)用longen.getName這個方法的時候阱高,我們返回this.name,即我們可以認(rèn)為返回的是Yunxi.name 因此返回的是 tugenhua茬缩,我們只是借用了下Yunxi這個對象內(nèi)的this.name來保存Longen傳進(jìn)去的參數(shù)而已;

二:閉包的理解

閉包的結(jié)構(gòu)有如下2個特性

1.封閉性:外界無法訪問閉包內(nèi)部的數(shù)據(jù)吼旧,如果在閉包內(nèi)聲明變量凰锡,外界是無法訪問的,除非閉包主動向外界提供訪問接口圈暗;

2.持久性:一般的函數(shù)掂为,調(diào)用完畢之后,系統(tǒng)自動注銷函數(shù)员串,而對于閉包來說勇哗,在外部函數(shù)被調(diào)用之后,閉包結(jié)構(gòu)依然保存在

系統(tǒng)中寸齐,閉包中的數(shù)據(jù)依然存在欲诺,從而實(shí)現(xiàn)對數(shù)據(jù)的持久使用。

缺點(diǎn):

使用閉包會占有內(nèi)存資源渺鹦,過多的使用閉包會導(dǎo)致內(nèi)存溢出等.

如下代碼:

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

首先在a函數(shù)內(nèi)定義了2個變量扰法,1個是存儲參數(shù),另外一個是閉包結(jié)構(gòu)毅厚,在閉包結(jié)構(gòu)中保存著b函數(shù)內(nèi)的a變量塞颁,默認(rèn)情況下,當(dāng)a函數(shù)調(diào)用完之后a變量會自動銷毀的吸耿,但是由于閉包的影響祠锣,閉包中使用了外界的變量,因此a變量會一直保存在內(nèi)存當(dāng)中咽安,因此變量a參數(shù)沒有隨著a函數(shù)銷毀而被釋放伴网,因此引申出閉包的缺點(diǎn)是:過多的使用閉包會占有內(nèi)存資源,或內(nèi)存溢出等肯能性板乙;

// 經(jīng)典的閉包實(shí)列如下:
function f(x){              //外部函數(shù)
    var a = x;              // 外部函數(shù)的局部變量是偷,并傳遞參數(shù)
    var b = function(){     // 內(nèi)部函數(shù) 
        return a;           // 訪問外部函數(shù)中的局部變量
    };
    a++;                    // 訪問后,動態(tài)更新外部函數(shù)的變量
    return b;               // 返回內(nèi)部函數(shù)
}
var c = f(5);               // 調(diào)用外部函數(shù)并且賦值
console.log(c());           // 調(diào)用內(nèi)部函數(shù)募逞,返回外部函數(shù)更新后的值為6

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

在如下代碼中有2個函數(shù)蛋铆,f函數(shù)的功能是:把數(shù)組類型的參數(shù)中每個元素的值分別封裝在閉包結(jié)構(gòu)中,然后把閉包存儲在一個數(shù)組中放接,并返回這個數(shù)組刺啦,但是在函數(shù)e中調(diào)用函數(shù)f并向其傳遞一個數(shù)組["a","b","c"],然后遍歷返回函數(shù)f返回數(shù)組,我們運(yùn)行打印后發(fā)現(xiàn)都是c undefined纠脾,那是因?yàn)樵趫?zhí)行f函數(shù)中的循環(huán)時候玛瘸,把值雖然保存在temp中蜕青,但是每次循環(huán)后temp值在不斷的變化,當(dāng)for循環(huán)結(jié)束后糊渊,此時temp值為c右核,同時i變?yōu)?,因此當(dāng)調(diào)用的時候 打印出來的是temp為3渺绒,arrs[3]變?yōu)閡ndefined贺喝;因此打印出 c undefined

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

// 代碼一
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的指向常見的有如下幾點(diǎn)需要常用到:

  1. 全局對象的this是指向與window躏鱼;
  2. 作為普通函數(shù)調(diào)用。
  3. 作為對象方法調(diào)用殷绍。
  4. 構(gòu)造器調(diào)用染苛。
  5. Function.prototype.callFunction.prototype.apply調(diào)用。

下面我們分別來介紹一下

1.全局對象的this主到;

console.log(this); // this指向于window
setTimeout() 和 setInterval()函數(shù)內(nèi)部的this指針是指向于window的茶行,如下代碼:
 function test(){
    console.log(11);
 }
 setTimeout(function(){
    console.log(this === window); // true
    this.test(); // 11 
 });

2.作為普通函數(shù)調(diào)用;

如下代碼:

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

當(dāng)作為普通函數(shù)調(diào)用時候镰烧,this總是指向了全局對象拢军,在瀏覽器當(dāng)中,全局對象一般指的是window怔鳖;

3.作為對象的方法調(diào)用茉唉。

如下代碼:

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

但是呢籽孙,我們不能像如下一樣調(diào)用對象了拢锹,如下調(diào)用對象的話塘雳,this還是執(zhí)行了window揍鸟,如下代碼:

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

運(yùn)行yunxi()函數(shù)犀呼,還是會像調(diào)用普通函數(shù)一樣蜡感,this指向了window的蹬蚁;

4.構(gòu)造器調(diào)用。
Javascript中不像Java一樣郑兴,有類的概念犀斋,而JS中只能通過構(gòu)造器創(chuàng)建對象,通過new 對象情连,當(dāng)new運(yùn)算符調(diào)用函數(shù)時候叽粹,該函數(shù)會返回一個對象,一般情況下,構(gòu)造器里面的this就是指向返回的這個對象虫几;

如下代碼:

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

注意:構(gòu)造器函數(shù)第一個字母需要大寫锤灿,這是為了區(qū)分普通函數(shù)還是構(gòu)造器函數(shù)而言;

如上代碼:通過調(diào)用new Obj()方法 返回值保存到test變量中辆脸,那么test就是那個對象了但校,所以內(nèi)部的this就指向與test對象了,因此test.name就引用到了內(nèi)部的this.name 即輸出 “yunxi”字符串啡氢;

但是也有例外的情況始腾,比如構(gòu)造器顯示地返回了一個對象的話,那么這次繼續(xù)調(diào)用的話空执,那么會最終會返回這個對象,比如如下代碼:

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

那么繼續(xù)調(diào)用的話穗椅,會返回unedfined辨绊,因?yàn)榉祷氐氖悄莻€對象,對象里面沒有name這個屬性匹表,因此值為undefined门坷;

四:理解函數(shù)引用和函數(shù)調(diào)用的區(qū)別

看下面的代碼分析:

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

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

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

如上的代碼:代碼一和代碼二分部是函數(shù)引用和函數(shù)調(diào)用的列子,返回都為true袍镀,代碼三也是函數(shù)調(diào)用的列子默蚌,返回且為false

我們現(xiàn)在來理解下函數(shù)引用和函數(shù)調(diào)用的本質(zhì)區(qū)別:當(dāng)引用函數(shù)時候,多個變量內(nèi)存存儲的是函數(shù)的相同的入口指針苇羡,因此對于同一個函數(shù)來講绸吸,無論多少個變量引用,他們都是相等的设江,因?yàn)閷τ谝妙愋?對象锦茁,數(shù)組,函數(shù)等)都是比較的是內(nèi)存地址叉存,如果他們內(nèi)存地址一樣的話码俩,說明是相同的;但是對于函數(shù)調(diào)用來講歼捏,比如代碼三;每次調(diào)用的時候稿存,都被分配一個新的內(nèi)存地址,所以他們的內(nèi)存地址不相同瞳秽,因此他們會返回false瓣履,但是對于代碼二來講,我們看到他們沒有返回函數(shù)寂诱,只是返回數(shù)值拂苹,他們比較的不是內(nèi)存地址,而是比較值,所以他們的值相等瓢棒,因此他們也返回true浴韭,我們也可以看看如下實(shí)列化一個對象的列子,他們也被分配到不同的內(nèi)存地址脯宿,因此他們也是返回false的念颈;如下代碼測試:

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

五:理解js中的鏈?zhǔn)秸{(diào)用

我們使用jquery的時候,jquery的簡單的語法及可實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用方法连霉,現(xiàn)在我們自己也封裝一個鏈?zhǔn)秸{(diào)用的方法榴芳,來理解下 jquery中如何封裝鏈?zhǔn)秸{(diào)用 無非就是每次調(diào)用一個方法的時候 給這個方法返回this即可,this指向該對象自身跺撼,我們看看代碼:

// 定義一個簡單的對象窟感,每次調(diào)用對象的方法的時候,該方法都返回該對象自身
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擴(kuò)展類型添加方法的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("鏈?zhǔn)秸{(diào)用");
    return this;
});
String.method('r',function(){
    return this.replace(/a/,'');
});
var str = " abc ";
console.log(str.trim().log2().r()); // 輸出鏈?zhǔn)秸{(diào)用和 bc

六:理解使用函數(shù)實(shí)現(xiàn)歷史記錄--提高性能

函數(shù)可以使用對象去記住先前操作的結(jié)果歉井,從而避免多余的運(yùn)算柿祈。比如我們現(xiàn)在測試一個費(fèi)波納茨的算法,我們可以使用遞歸函數(shù)計算fibonacci數(shù)列哩至,一個fibonacci數(shù)字是之前兩個fibonacci數(shù)字之和躏嚎,最前面的兩個數(shù)字是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函數(shù)總共調(diào)用了453次菩貌,for循環(huán)了11次卢佣,它自身調(diào)用了442次,如果我們使用下面的記憶函數(shù)的話箭阶,那么就可以減少他們的運(yùn)算次數(shù)虚茶,從而提高性能;

思路:先使用一個臨時數(shù)組保存存儲結(jié)果仇参,當(dāng)函數(shù)被調(diào)用的時候媳危,先看是否已經(jīng)有存儲結(jié)果 如果有的話,就立即返回這個存儲結(jié)果冈敛,否則的話待笑,調(diào)用函數(shù)運(yùn)算下;代碼如下:

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

這個函數(shù)也返回了同樣的結(jié)果抓谴,但是只調(diào)用了函數(shù)29次暮蹂,循環(huán)了11次,也就是說函數(shù)自身調(diào)用了18次癌压,從而減少無謂的函數(shù)的調(diào)用及運(yùn)算仰泻,下面我們可以把這個函數(shù)進(jìn)行抽象化,以構(gòu)造帶記憶功能的函數(shù)滩届,如下代碼:

var count3 = 0;
var memoizer = function(memo,formula) {
    var recur = function(n) {
        var result = memo[n];
        count3++;   // 這句代碼只是說明運(yùn)行函數(shù)多少次集侯,在代碼中并無作用,實(shí)際使用上可以刪掉
        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);
 });
 // 調(diào)用方式如下
 for(var k = 0; k <=10; k+=1) {
    console.log(k+":"+fibonacci3(k));
 }
 console.log(count3); // 29

如上封裝 memoizer 里面的參數(shù)是實(shí)現(xiàn)某個方法的計算公式,具體的可以根據(jù)需要自己手動更改棠枉,這邊的思路無非就是想習(xí)慣使用對象去保存臨時值浓体,從而減少不必要的取值存儲值的操作;

七:理解通過Function擴(kuò)展類型

javascript 允許為語言的基本數(shù)據(jù)類型定義方法辈讶。通過Object.prototype添加原型方法命浴,該方法可被所有的對象使用。

這對函數(shù)贱除,字符串生闲,數(shù)字,正則和布爾值都適用月幌,比如如下現(xiàn)在給Function.prototype增加方法碍讯,使該方法對所有函數(shù)都可用,代碼如下:

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

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

使用函數(shù)和閉包可以構(gòu)建模塊扯躺,所謂模塊冲茸,就是一個提供接口卻隱藏狀態(tài)與實(shí)現(xiàn)的函數(shù)或?qū)ο蟆J褂煤瘮?shù)構(gòu)建模塊的優(yōu)點(diǎn)是:減少全局變量的使用缅帘;

比如如下:我想為String擴(kuò)展一個方法,該方法的作用是尋找字符串中的HTML字符字體并將其替換為對應(yīng)的字符难衰;

// 如下代碼:
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()); // <">

模塊模式利用函數(shù)作用域和閉包來創(chuàng)建綁定對象與私有成員的關(guān)聯(lián)钦无,比如在上面的deentityify()方法才有權(quán)訪問字符實(shí)體表entity這個數(shù)據(jù)對象;

模塊開發(fā)的一般形式是:定義了私有變量和函數(shù)的函數(shù)盖袭,利用閉包創(chuàng)建可以訪問到的私有變量和函數(shù)的特權(quán)函數(shù)失暂,最后返回這個特權(quán)函數(shù),或把他們保存到可以訪問的地方鳄虱。

模塊模式一般會結(jié)合實(shí)例模式使用弟塞。javascript的實(shí)例就是使用對象字面量表示法創(chuàng)建的。對象的屬性值可以是數(shù)值或者函數(shù)拙已,并且屬性值在該對象的生命周期中不會發(fā)生變化决记;比如如下代碼屬于模塊模式:定義了一個私有變量name屬性,和一個實(shí)例模式(對象字面量obj)并且返回這個對象字面量obj倍踪,對象字面量中的方法與私有變量name進(jìn)行了綁定系宫;

// 比如如下經(jīng)典的模塊模式
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

九:理解惰性實(shí)列化

在頁面中javascript初始化執(zhí)行的時候就實(shí)例化類,如果在頁面中沒有使用這個實(shí)列化的對象建车,就會造成一定的內(nèi)存浪費(fèi)和性能損耗扩借;這時候,我們可以使用惰性實(shí)列化來解決這個問題缤至,惰性就是把實(shí)列化推遲到需要使用它的時候才去做潮罪,做到 "按需供應(yīng)";

// 惰性實(shí)列化代碼如下
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實(shí)列
    var instance;
    return {
        init: function(){
            // 如果不存在實(shí)列,就創(chuàng)建單列實(shí)列
            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;
        }
    }
}();
// 調(diào)用方式
myNamespace.init();
var name = myNamespace.getName();
console.log(name); // tugenhua

如上代碼是惰性化實(shí)列代碼:它包括一個單體Configure實(shí)列,直接返回init函數(shù)嫉到,先判斷該單體是否被實(shí)列化沃暗,如果沒有被實(shí)列化的話,則創(chuàng)建并執(zhí)行實(shí)列化并返回該實(shí)列化屯碴,如果已經(jīng)實(shí)列化了描睦,則返回現(xiàn)有實(shí)列;執(zhí)行完后导而,則銷毀init方法忱叭,只初始化一次

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

分支函數(shù)的作用是:可以解決兼容問題if或者else的重復(fù)判斷的問題,我們一般的做法是:根據(jù)兼容的不同寫if今艺,else等韵丑,這些判斷來實(shí)現(xiàn)兼容,但是這樣明顯就有一個缺點(diǎn)虚缎,每次執(zhí)行這個函數(shù)的時候撵彻,都需要進(jìn)行if和else的檢測,效率明顯不高实牡,我們現(xiàn)在使用分支函數(shù)來實(shí)現(xiàn)當(dāng)初始化的時候進(jìn)行一些檢測陌僵,在之后的運(yùn)行代碼過程中,代碼就無需檢測了创坞;

// 我們先來看看傳統(tǒng)的封裝ajax請求的函數(shù)
//創(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");
    }
}
// 下面我們看看分支函數(shù)代碼如下:
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對象

上面的代碼就是分支函數(shù)碗短,分支的原理是:聲明幾個不同名稱的對象,且為該不同名稱對象聲明一個相同的方法题涨,然后根據(jù)不同的瀏覽器設(shè)計來實(shí)現(xiàn)偎谁,接著開始進(jìn)行瀏覽器檢測,并且根據(jù)瀏覽器檢測來返回哪一個對象纲堵,不論返回的是哪一個對象巡雨,最后它一致對外的接口都是createXHR方法的;

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

和上面分支的原理是一樣的席函,代碼也可以按照上面的推薦分支風(fēng)格編碼的铐望;解決的問題也是解決多個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);
};

惰性載入函數(shù)也是在函數(shù)內(nèi)部改變自身的一種方式茂附,在重復(fù)執(zhí)行的時候就不會再進(jìn)行檢測的蝌以;惰性載入函數(shù)的分支只會執(zhí)行一次,即第一次調(diào)用的時候何之,其優(yōu)點(diǎn)如下:

  1. 要執(zhí)行的適當(dāng)代碼只有在實(shí)際調(diào)用函數(shù)時才執(zhí)行跟畅。

  2. 第一次調(diào)用該函數(shù)的時候,緊接著內(nèi)部函數(shù)也會執(zhí)行溶推,但是正因?yàn)檫@個徊件,所以后續(xù)繼續(xù)調(diào)用該函數(shù)的話奸攻,后續(xù)的調(diào)用速度會很快;因此避免了多重條件虱痕;

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

DOM操作的交互需要更多的內(nèi)存和CPU時間睹耐,連續(xù)進(jìn)行過多的DOM相關(guān)的操作可能會導(dǎo)致瀏覽器變慢甚至崩潰,函數(shù)節(jié)流的設(shè)計思想是讓某些代碼可以在間斷的情況下連續(xù)重復(fù)執(zhí)行部翘,實(shí)現(xiàn)該方法可以使用定時器對該函數(shù)進(jìn)行節(jié)流操作;

比如:第一次調(diào)用函數(shù)的時候硝训,創(chuàng)建一個定時器,在指定的時間間隔下執(zhí)行代碼新思。當(dāng)?shù)诙螆?zhí)行的時候窖梁,清除前一次的定時器并設(shè)置另一個,將其替換成一個新的定時器;

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

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

函數(shù)節(jié)流解決的問題是一些代碼(比如事件)無間斷的執(zhí)行夹囚,這可能會影響瀏覽器的性能纵刘,比如瀏覽器變慢或者直接崩潰。比如對于mouseover事件或者click事件荸哟,比如點(diǎn)擊tab項(xiàng)菜單假哎,無限的點(diǎn)擊,有可能會導(dǎo)致瀏覽器會變慢操作鞍历,這時候我們可以使用函數(shù)節(jié)流的操作來解決舵抹;
原文地址:http://www.cnblogs.com/tugenhua0707/p/5046854.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市劣砍,隨后出現(xiàn)的幾起案子惧蛹,更是在濱河造成了極大的恐慌,老刑警劉巖秆剪,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異爵政,居然都是意外死亡仅讽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門钾挟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洁灵,“玉大人,你說我怎么就攤上這事掺出』涨В” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵汤锨,是天一觀的道長双抽。 經(jīng)常有香客問我,道長闲礼,這世上最難降的妖魔是什么牍汹? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任铐维,我火速辦了婚禮,結(jié)果婚禮上慎菲,老公的妹妹穿的比我還像新娘嫁蛇。我一直安慰自己,他們只是感情好露该,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布睬棚。 她就那樣靜靜地躺著,像睡著了一般解幼。 火紅的嫁衣襯著肌膚如雪抑党。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天书幕,我揣著相機(jī)與錄音新荤,去河邊找鬼。 笑死台汇,一個胖子當(dāng)著我的面吹牛苛骨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苟呐,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痒芝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牵素?” 一聲冷哼從身側(cè)響起严衬,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎笆呆,沒想到半個月后请琳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赠幕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年俄精,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榕堰。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡竖慧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逆屡,到底是詐尸還是另有隱情圾旨,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布魏蔗,位于F島的核電站砍的,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏莺治。R本人自食惡果不足惜挨约,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一味混、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诫惭,春花似錦翁锡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怨绣,卻和暖如春角溃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篮撑。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工减细, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赢笨。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓未蝌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茧妒。 傳聞我的和親對象是個殘疾皇子萧吠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容