JavaScript中的高級函數(shù)
在JavaScript中,函數(shù)的功能十分強(qiáng)大。它們是第一類對象,也可以作為另一個對象的方法,還可以作為參數(shù)傳入另一個函數(shù),不僅如此,還能被一個函數(shù)返回!可以說,在JS中,函數(shù)無處不在,無所不能,堪比孫猴子呀!當(dāng)你運用好函數(shù)時,它能助你取西經(jīng),讓代碼變得優(yōu)雅簡潔,運用不好時,那就遭殃了,要大鬧天宮咯~
除了函數(shù)相關(guān)的基礎(chǔ)知識外,掌握一些高級函數(shù)并應(yīng)用起來,不僅能讓JS代碼看起來更為精簡,還可以提升性能。以下是小編總結(jié)的一些常用的、重要的高級函數(shù),加上了一些個人見解,特此記錄下來。如果您是JS初學(xué)者,也不要被“高級”兩個字嚇到,因為文中穿插講解了一些原型、this等基礎(chǔ)知識,相信并不難理解。如果您是JS大牛,也可以把本文用來查漏補(bǔ)缺。
正文
作用域安全的構(gòu)造函數(shù)
function Person(name,age){
this.name = name;
this.age = age;
}
var p1 = new Person("Claiyre",80);
相信您對上面的構(gòu)造函數(shù)一定不陌生,但是,,如果某個粗心的程序猿調(diào)用這個構(gòu)造函數(shù)時忘記加 new 了會發(fā)生什么?
var p3 = Person("Tom",30);
console.log(p3); //undefined
console.log(window.name); //Tom
由于使用了不安全的構(gòu)造函數(shù),上面的代碼意外的改變了window的name,因為 this 對象是在運行時綁定的,使用new調(diào)用構(gòu)造函數(shù)時 this 是指向新創(chuàng)建的對象的,不使用 new 時, this 是指向window的。
由于window的name屬性是用來識別鏈接目標(biāo)和frame的,所在這里對該屬性的偶然覆蓋可能導(dǎo)致其他錯誤。
作用域安全的構(gòu)造函數(shù)會首先確認(rèn) this 對象是正確類型的實例,然后再進(jìn)行更改,如下:
function Person(name,age){
if(this instanceof Person){
this.name = name;
this.age = age;
} else {
return new Person(name,age);
}
}
這樣就避免了在全局對象上意外更改或設(shè)置屬性。
實現(xiàn)這個安全模式,相當(dāng)于鎖定了調(diào)用構(gòu)造函數(shù)的環(huán)境,因此借用構(gòu)造函數(shù)繼承模式可能會出現(xiàn)問題,解決方法是組合使用原型鏈和構(gòu)造函數(shù)模式,即組合繼承。
如果您是一個JS庫或框架的開發(fā)者,相信作用域安全的構(gòu)造函數(shù)一定對您非常有用。在多人協(xié)作的項目中,為了避免他們誤改了全局對象,也應(yīng)使用作用域安全的構(gòu)造函數(shù)。
惰性載入函數(shù)
由于瀏覽器間的行為差異,代碼中可能會有許多檢測瀏覽器行為的if語句。但用戶的瀏覽器若支持某一特性,便會一直支持,所以這些if語句,只用被執(zhí)行一次,即便只有一個if語句的代碼,也比沒有要快。
惰性載入表示函數(shù)執(zhí)行的分支僅會執(zhí)行一次,有兩種實現(xiàn)惰性載入的方式,第一種就是在函數(shù)第一次被調(diào)用時再處理函數(shù),用檢測到的結(jié)果重寫原函數(shù)。
function detection(){
if(//支持某特性){
detection = function(){
//直接用支持的特性
}
} else if(//支持第二種特性){
detection = function(){
//用第二種特性
}
} else {
detection = function(){
//用其他解決方案
}
}
}
第二種實現(xiàn)惰性載入的方式是在聲明函數(shù)時就指定適當(dāng)?shù)暮瘮?shù)
var detection = (function(){
if(//支持某特性){
return function(){
//直接用支持的特性
}
} else if(//支持第二種特性){
return function(){
//用第二種特性
}
} else {
return function(){
//用其他解決方案
}
}
})();
惰性載入函數(shù)的有點是在只初次執(zhí)行時犧牲一點性能,之后便不會再有多余的消耗性能。
函數(shù)綁定作用域
在JS中,函數(shù)的作用域是在函數(shù)被調(diào)用時動態(tài)綁定的,也就是說函數(shù)的this對象的指向是不定的,但在一些情況下,我們需要讓某一函數(shù)的執(zhí)行作用域固定,總是指向某一對象。這時怎么辦呢?
當(dāng)當(dāng)當(dāng)~~可以用函數(shù)綁定作用域函數(shù)呀
function bind(fn,context){
return function(){
return fn.apply(context,arguments);
}
}
用法:
var person1 = {
name: "claiyre",
sayName: function(){
alert(this.name);
}
}
var sayPerson1Name = bind(person1.sayName,person1);
sayPerson1Name(); //claiyre
call 函數(shù)和 apply 函數(shù)可以臨時改變函數(shù)的作用域,使用bind函數(shù)可以得到一個綁定了作用域的函數(shù)
函數(shù)柯里化(curry)
curry的概念很簡單:只傳遞部分參數(shù)來調(diào)用函數(shù),然后讓函數(shù)返回另一個函數(shù)去處理剩下的參數(shù)??梢岳斫鉃橘x予了函數(shù)“加載”的能力。
許多js庫中都封裝了curry函數(shù),具體使用可以這樣。
var match = curry(function(what,str){
return str.match(what)
});
var hasNumber = match(/[0-9]+/g);
var hasSpace = match(/\s+/g)
hasNumber("123asd"); //['123']
hasNumber("hello world!"); //null
hasSpace("hello world!"); //[' '];
hasSpace("hello"); //null
console.log(match(/\s+/g,'i am Claiyre')); //直接全部傳參也可: [' ',' ']
一旦函數(shù)經(jīng)過柯里化,我們就可以先傳遞部分參數(shù)調(diào)用它,然后得到一個更具體的函數(shù)。這個更具體的函數(shù)通過閉包幫我們記住了第一次傳遞的參數(shù),最后我們就可以用這個更具體的函數(shù)為所欲為啦~
一個較為簡單的實現(xiàn)curry的方式:
function curry(fn){
var i = 0;
var outer = Array.prototype.slice.call(arguments,1);
var len = fn.length;
return function(){
var inner = outer.concat(Array.prototype.slice.call(arguments));
return inner.length === len?fn.apply(null,inner):function (){
var finalArgs = inner.concat(Array.prototype.slice.call(arguments));
return fn.apply(null,finalArgs);
}
}
}
debounce函數(shù)
debounce函數(shù),又稱“去抖函數(shù)”。它的功能也很簡單直接,就是防止某一函數(shù)被連續(xù)調(diào)用,從而導(dǎo)致瀏覽器卡死或崩潰。用法如下:
var myFunc = debounce(function(){
//繁重、耗性能的操作
},250);
window.addEventListener('resize',myFunc);
像窗口的resize,這類可以以較高的速率觸發(fā)的事件,非常適合用去抖函數(shù),這時也可稱作“函數(shù)節(jié)流”,避免給瀏覽器帶來過大的性能負(fù)擔(dān)。
具體的實現(xiàn)時,當(dāng)函數(shù)被調(diào)用時,不立即執(zhí)行相應(yīng)的語句,而是等待固定的時間w,若在w時間內(nèi),即等待還未結(jié)束時,函數(shù)又被調(diào)用了一次,則再等待w時間,重復(fù)上述過程,直到最后一次被調(diào)用后的w時間內(nèi)該函數(shù)都沒有被再調(diào)用,則執(zhí)行相應(yīng)的代碼。
實現(xiàn)代碼如下:
function debounce(fn,wait){
var td;
return function(){
clearTimeout(td);
td= setTimeout(fn,wait);
}
}
once函數(shù)
顧名思義,once函數(shù)是僅僅會被執(zhí)行一次的函數(shù)。具體實現(xiàn)如下:
function once(fn){
var result;
return function(){
if(fn){
result = fn(arguments);
fn = null;
}
return result;
}
}
var init = once(function(){
//初始化操作
})
在被執(zhí)行過一次后,參數(shù)fn就被賦值null了,那么在接下來被調(diào)用時,便再也不會進(jìn)入到if語句中了,也就是第一次被調(diào)用后,該函數(shù)永遠(yuǎn)不會被執(zhí)行了。
還可以對上述once函數(shù)進(jìn)行改進(jìn),不僅可以傳入函數(shù),同時還可以給傳入的函數(shù)綁定作用域u,同時實現(xiàn)了bind和once。
function once(fn,context){
var result;
return function(){
if(fn){
result = fn.apply(context,arguments);
fn = null;
}
return result;
}
}
結(jié)語
通過以上的閱讀,不難發(fā)現(xiàn)很多“高級函數(shù)”的實現(xiàn)其實并不復(fù)雜,數(shù)十行代碼便可搞定,但重要的是能真正理解它們的原理,在實際中適時地應(yīng)用,以此性能提升,讓代碼簡潔,邏輯清晰
- javascript性能優(yōu)化之分時函數(shù)的介紹
- 深入解析JavaScript中函數(shù)的Currying柯里化
- javascript中有趣的反柯里化深入分析
- JavaScript函數(shù)柯里化詳解
- javascript中利用柯里化函數(shù)實現(xiàn)bind方法
- 深入剖析JavaScript中的函數(shù)currying柯里化
- javascript中利用柯里化函數(shù)實現(xiàn)bind方法【推薦】
- 深入淺出理解JavaScript高級定時器原理與用法
- Js setInterval與setTimeout(定時執(zhí)行與循環(huán)執(zhí)行)的代碼(可以傳入?yún)?shù))
- js定時器的使用(實例講解)
- JavaScript高級函數(shù)應(yīng)用之分時函數(shù)實例分析
相關(guān)文章
Javascript中匿名函數(shù)的調(diào)用與寫法實例詳解(多種)
js中定義函數(shù)的方式有很多種,函數(shù)直接量就是其中一種,下面通過本文給大家介紹匿名函數(shù)是如何調(diào)用的及匿名函數(shù)的n中寫法,對js匿名函數(shù)調(diào)用,js匿名函數(shù)寫法相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2016-01-01
drag-and-drop實現(xiàn)圖片瀏覽器預(yù)覽
chrome的drag and drop API,它能將本地的圖片放到瀏覽器中進(jìn)行預(yù)覽,猜想一下當(dāng)我們把圖片拖拽到瀏覽器里會發(fā)生什么事情,你的瀏覽器試圖打開一個新的頁面并加載這個圖片。這篇文章給我們介紹drag-and-drop實現(xiàn)圖片瀏覽器預(yù)覽,需要的朋友可以參考下2015-08-08
window.parent調(diào)用父框架時 ie跟火狐不兼容問題
window.parent調(diào)用父框架時,ie跟火狐不兼容問題!2009-07-07
JS實現(xiàn)動態(tài)生成html table表格的方法分析
這篇文章主要介紹了JS實現(xiàn)動態(tài)生成html table表格的方法,結(jié)合實例形式分析了javascript針對數(shù)組數(shù)據(jù)的讀取、遍歷以及動態(tài)生成相關(guān)操作技巧,需要的朋友可以參考下2018-07-07

