一:理解call和apply 及arguments.callee
ECMAScript3給Function的原型定義了兩個方法阔加,他們是Function.prototype.call
和 Function.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)需要常用到:
- 全局對象的this是指向與window躏鱼;
- 作為普通函數(shù)調(diào)用。
- 作為對象方法調(diào)用殷绍。
- 構(gòu)造器調(diào)用染苛。
-
Function.prototype.call
或Function.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)如下:
要執(zhí)行的適當(dāng)代碼只有在實(shí)際調(diào)用函數(shù)時才執(zhí)行跟畅。
第一次調(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