国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript設(shè)計模式與開發(fā)實踐 | 03 - 閉包和高階函數(shù)

Leck1e / 2379人閱讀

摘要:閉包閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式,就是在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)。在這里產(chǎn)生了一個閉包結(jié)構(gòu),局部變量的生命周期被延續(xù)了。本節(jié)內(nèi)容為設(shè)計模式與開發(fā)實踐第三章筆記。

閉包

閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。

創(chuàng)建閉包的常見方式,就是在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)。閉包的形成與變量的作用域以及變量的生存周期有關(guān)。

變量的作用域

變量的作用域就是指變量的有效范圍。

當在函數(shù)中聲明一個變量時,如果變量前面沒有帶上關(guān)鍵字var,這個變量就會成為全局變量;如果用var關(guān)鍵字在函數(shù)中聲明變量,這個變量就是局部變量,只有在該函數(shù)內(nèi)部才能訪問到這個變量,在函數(shù)外部是訪問不到的。

在JavaScript中,函數(shù)可以用來創(chuàng)造函數(shù)作用域。在函數(shù)里面可以看到外面的變量,而在函數(shù)外面則無法看到函數(shù)里面的變量。這是因為當在函數(shù)中搜索一個變量的時候,如果該函數(shù)內(nèi)并沒有聲明這個變量,那么此次搜索的過程會隨著代碼的執(zhí)行環(huán)境創(chuàng)建的作用域鏈往外層逐層搜索,一直搜索到全局對象。變量的搜索是從內(nèi)到外的。

var a = 1;

var func1 = function(){
  var b = 2;
  var func2 = function(){
      var c = 3;
      console.log(b);  // 輸出:2
      console.log(c);  // 輸出:1
  }
  func2();
  console.log(c);  // 變量c在函數(shù)內(nèi)部,是局部變量,此時在外部訪問不到。 輸出:Uncaught ReferenceError: c is not defined
};

func1();
變量的生存周期

全局變量的生存周期是永久的,除非我們主動銷毀這個全局變量。而在函數(shù)內(nèi)用var關(guān)鍵字聲明的局部變量,當退出函數(shù)時,這些局部變量即失去了它們的價值,會隨著函數(shù)調(diào)用的結(jié)束而被銷毀:

var func = function(){
  var a = 1;  // 退出函數(shù)后局部變量a將被銷毀
  console.log(a);  // 輸出:1
};

func();

但是,有一種情況卻跟我們的推論相反。

var func = function(){
  var a = 1;  //函數(shù)外部訪問不到局部變量a,退出函數(shù)后,局部變量a被銷毀
  console.log(a);  // 輸出:1
};

func();  
console.log(a);  // 輸出:Uncaught ReferenceError: a is not defined


var func = function(){
  var a = 1;
  return function(){
      a++;
      console.log(a);
  }
};

var f = func();

f();  // 輸出:2
f();  // 輸出:3
f();  // 輸出:4
f();  // 輸出:5

當退出函數(shù)后,局部變量a并沒有消失,而是似乎一直在某個地方存活著。這是因為當執(zhí)行 var f = func(); 時,f返回了一個匿名函數(shù)的引用,它可以訪問到func()被調(diào)用時產(chǎn)生的環(huán)境,而布局變量a一直處在這個環(huán)境里。既然局部變量所在的環(huán)境還能被外界訪問,這個局部變量就有了不被銷毀的理由。在這里產(chǎn)生了一個閉包結(jié)構(gòu),局部變量的生命周期被延續(xù)了。

閉包的作用

封裝變量

延續(xù)局部變量的壽命

1. 封裝變量

閉包可以幫助把一些不需要暴露在全局的變量封裝成“私有變量”。

假設(shè)有一個計算乘積的函數(shù):

var cache = {};
var mult = function(){
  var args = Array.prototype.join.call(arguments, ",");
  if(cache[args]){
      return cache[args];
  }
  var a = 1;
  for(var i=0, l=arguments.length; i< l; i++){
      a = a * arguments[i];
  }
  return cache[args] = a;
};

console.log(mult(1,2,3));  // 輸出:6
console.log(mult(1,2,3));  // 輸出:6

我們看到cache這個變量僅僅在mult函數(shù)中被使用,與其讓cache變量跟mult函數(shù)一起平行地暴露在全局作用域下,不如把它封閉在mult函數(shù)內(nèi)部,這樣可以減少頁面中的全局變量,以避免這個變量在其他地方被不小心修改而引發(fā)錯誤。

var mult = (function(){
  var cache = {};
  return function(){
      var args = Array.prototype.join.call(arguments, ",");
      if(args in cache){
        return cache[args];
      }
      var a = 1;
      for(var i=0, l=arguments.length; i

提煉函數(shù)是代碼重構(gòu)中的一種常見技巧。如果在一個大函數(shù)中有一些代碼能夠獨立出來,就把這些代碼封裝在獨立的小函數(shù)里。獨立出來的小函數(shù)有助于代碼服用。

var mult = (function(){
  var cache = {};
  var calculate = function(){
      var a = 1;
      for(var i=0, l=arguments.length; i

2.延續(xù)局部變量的壽命
img對象常用于進行數(shù)據(jù)上報,如下:

var report = function(src) {
  var img = new Image();
  img.src = src;
};

report("http://xxx.com/getUserInfo");

一些低版本瀏覽器的實現(xiàn)存在bug,在這些瀏覽器中使用report函數(shù)進行數(shù)據(jù)上報會丟失30%左右的數(shù)據(jù),也就是說,report函數(shù)并不是每一次都成功發(fā)起了HTTP請求。丟失數(shù)據(jù)的原因是img是report函數(shù)中的局部變量,當report函數(shù)的調(diào)用結(jié)束后,img局部變量隨即被銷毀,而此時或許還沒來得及發(fā)出HTTP請求,所以此次請求就會丟失掉。

把img變量用閉包封閉起來:

var report =(function(){
  var imgs = [];
  return function(src) {
    var img  = new Image();
    imgs.push(img);
    img.src = src;
  }
})();
閉包和面向?qū)ο笤O(shè)計

過程與數(shù)據(jù)的結(jié)合是形容面向?qū)ο笾械摹皩ο蟆睍r經(jīng)常使用的表達。對象以方法的形式包含了過程,而閉包則是在過程中以環(huán)境的形式包含了數(shù)據(jù)。通常用面對對象思想能實現(xiàn)的功能,用閉包也能實現(xiàn),反之亦然。

看看這段面向?qū)ο髮懛ǖ拇a:

var extent = {
  value: 0;
  call: function(){
    this.value++;    
    console.log(this.value);
  }
};

// 作為對象的方法調(diào)用,this指向該對象
extent.call();  // 輸出:1
extent.call();  // 輸出:2
extent.call();  // 輸出:3

換成閉包的寫法如下:

var extent = function(){
  var value = 0;
  return {
      call: function(){
          value++;
          console.log(value);
      }
  }
};

var extent = extent();
extent.call();  // 輸出:1
extent.call();  // 輸出:2
extent.call();  // 輸出:3
閉包與內(nèi)存管理

局部變量本來應該在函數(shù)退出的時候就被解除引用,但如果局部變量被封閉在閉包形成的環(huán)境中,那么這個局部變量就能一直生存下去。從這個意義上看,閉包確實會使一些數(shù)據(jù)無法被及時銷毀。使用閉包的一部分原因是我們選擇主動把一些變量封閉在閉包中,因為可能在以后還需要使用這些變量,把這些對象放在閉包中和放在全局作用域中,對內(nèi)存方面的影響是一致的。如果在將來需要回收這些變量,可以手動把變量設(shè)為null。

使用閉包的同時比較容易造成循環(huán)引用,如果閉包的作用域鏈中保存著一些DOM節(jié)點,這時候就有可能造成內(nèi)存泄露。但這本身并非閉包的問題,也并非JavaScript的問題。在IE瀏覽器中,由于BOM和DOM中的對象是使用C++以COM對象的方式實現(xiàn)的,而COM對象的垃圾收集機制采用的是引用計數(shù)策略。在基于引用計數(shù)策略的垃圾回收機制中,如果兩個對象之間形成了循環(huán)引用,那么這兩個對象都無法被回收,但循環(huán)引用造成的內(nèi)存泄露在本質(zhì)上也不是閉包造成的。

如果要解決循環(huán)引用帶來的內(nèi)存泄露問題,我們只需要把循環(huán)引用中的變量設(shè)為null。將變量設(shè)為null,意味著切斷變量與它之前引用的值之間的連接。當垃圾收集器下次運行時,就會刪除這些值并回收它們占用的內(nèi)存。

高階函數(shù) 定義

高階函數(shù)是指至少滿足下列條件之一的函數(shù):

函數(shù)可以作為參數(shù)被傳遞;

函數(shù)可以作為返回值輸出。

函數(shù)作為參數(shù)傳遞

1. 回調(diào)函數(shù)

在ajax異步請求的應用中,回調(diào)函數(shù)的使用非常頻繁。當我們想在ajax請求返回之后做一些事情,但又不知道請求返回的確切時間時,最常見的方案就是把callback函數(shù)當作參數(shù)傳入發(fā)起的ajax請求的方法中,待請求完成之后執(zhí)行callback函數(shù):

var getUserInfo = function(){
  $.ajax("http://xxx.com/getUserInfo?" + userId, function(data){
      if(typeof callback === "function"){
          callback(data);
      }
  });
}

getUserInfo(13157, function(data){
  console.log(data.userName);
});

回調(diào)函數(shù)的應用不僅只在異步請求中,當一個函數(shù)不適合執(zhí)行一些請求時,我們也可以把這些請求封裝成一個函數(shù),并把它作為參數(shù)傳遞給另一個函數(shù),“委托”給另一個函數(shù)來執(zhí)行。

2. Array.prototype.sort

Array.prototype.sort接受一個函數(shù)當作參數(shù),這個函數(shù)里面封裝了數(shù)組元素的排序規(guī)則。從Array.prototype.sort的使用可以看到,我們的目的是對數(shù)組進行排序,這是不變的部分;而使用什么規(guī)則去排序,則是可變的部分。把可變的部分封裝在函數(shù)參數(shù)里,動態(tài)傳入Array.prototype.sort,使Array.prototype.sort方法成為了一個非常靈活的方法。

// 從小到大排序
console.log(    // 輸出:[1, 3, 4]
    [1, 4, 3].sort(function(a, b){
        return a - b;
    })
);

// 從大到小排序
console.log(  // 輸出:[4, 3, 1]
    [1, 4, 3].sort(function(a, b){
        return b - a;
    })
);
函數(shù)作為返回值輸出

1. 判斷數(shù)據(jù)的類型

判斷一個數(shù)據(jù)是否是數(shù)組,可以基于鴨子類型的理念來判斷,比如判斷這個數(shù)據(jù)有沒有l(wèi)ength熟悉,有沒有sort方法或者slice方法。但更好的方式是用Object.prototype.toString來計算。

Object.prototype.toString.call(obj)返回一個字符串,比如Object.prototype.toString.call([1,2,3])總是返回[Object Array],而Object.prototype.toString.call("str")總是返回[Object String]。所以我們可以編寫一系列isType函數(shù):

var isType = function(type){
  return function(obj){
      return Object.prototype.toString.call(obj) === "[object " + type + "]";
  }
};

var isString = isType("String");
var isArray = isType("Array");
var isNumber = isType("Number");

console.log(isArray([1,2,3]));   // 輸出:true

2. getSingle

有一種設(shè)計模式叫單例模式,下面是它的例子:

var getSingle = function(fn){
  var ret;
  return function(){
    return ret || (ret = fn.apply(this, arguments));
  };
};

var getScript = getSingle(function(){
  return document.createElement("script");
});

var script1 = getScript();
var script2 = getScript();

console.log(script1 === script2);  // 輸出:true

這個高階函數(shù)的例子,既把函數(shù)當作參數(shù)傳遞,又讓函數(shù)執(zhí)行后返回了另一個函數(shù)。

高階函數(shù)實現(xiàn)AOP

AOP(面向切面編程)的主要作用是把一些跟核心業(yè)務邏輯模塊無關(guān)的功能抽離出來,這些跟業(yè)務邏輯無關(guān)的功能通常包括日志統(tǒng)計、安全控制、異常處理等。把這些功能抽離處理之后,再通過“動態(tài)織入”的方式摻入業(yè)務邏輯模塊中。這樣做的好處首先是可以保持業(yè)務邏輯模塊的純凈和高內(nèi)聚性,其次是可以很方便地復用日志統(tǒng)計等功能模塊。

在JavaScript中實現(xiàn)AOP,都是指把一個函數(shù)“動態(tài)織入”到另一個函數(shù)之中,具體的實現(xiàn)技術(shù)有很多,這里我們通過擴展Function.prototype來實現(xiàn)。

function.prototype.before = function(beforefn){
  var __self = this;  // 保存原函數(shù)的引用
  return function(){  // 返回包含了原函數(shù)和新函數(shù)的“代理”函數(shù)
      beforefn.apply(this, arguments);  // 執(zhí)行新函數(shù),修正this
      return __self.apply(this, arguments);  // 執(zhí)行原函數(shù)
  }
};

Function.prototype.after = function(afterfn){
  var __self = this;
  return function(){
      var ret = __self.apply(this, arguments);
      afterfn.apply(this, arguments);
      return ret;
  }
};

var func = function(){
  console.log(2);
};

func = func.beforefn(function(){
  console.log(1);
}).after(function(){
  console.log(3);
});

func();

這種使用AOP的方式來給函數(shù)添加職責,也是JavaScript語言中一種非常特別和巧妙的裝飾者模式實現(xiàn)。

PS:本節(jié)內(nèi)容為《JavaScript設(shè)計模式與開發(fā)實踐》第三章 筆記。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86175.html

相關(guān)文章

  • JS學習理解之閉包高階函數(shù)

    摘要:閉包的形成與變量的作用域以及變量的生存周期密切相關(guān)。現(xiàn)在我們把變量用閉包封閉起來,便能解決請求丟失的問題二高階函數(shù)高階函數(shù)是指至少滿足下列條件之一的函數(shù)。回調(diào)函數(shù)在異步請求的應用中,回調(diào)函數(shù)的使用非常頻繁。 一、閉包 對于 JavaScript 程序員來說,閉包(closure)是一個難懂又必須征服的概念。閉包的形成與變量的作用域以及變量的生存周期密切相關(guān)。下面我們先簡單了解這兩個知識...

    張紅新 評論0 收藏0
  • 前端進擊的巨人(四):略知函數(shù)式編程

    摘要:自執(zhí)行函數(shù)閉包實現(xiàn)模塊化以樂之名程序員產(chǎn)品經(jīng)理對作用域,以及閉包知識還沒掌握的小伙伴,可回閱前端進擊的巨人三從作用域走進閉包。參考文檔利用閉包實現(xiàn)模塊化翻譯淺談中的高階函數(shù)系列更文請關(guān)注專欄前端進擊的巨人,不斷更新中。。。 系列更文前三篇文章,圍繞了一個重要的知識點:函數(shù)。函數(shù)調(diào)用棧、函數(shù)執(zhí)行上下文、函數(shù)作用域到閉包。可見不理解函數(shù)式編程,代碼都擼不好。 showImg(https:/...

    omgdog 評論0 收藏0
  • JavaScript深入淺出

    摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統(tǒng)的類繼承還要強大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...

    blair 評論0 收藏0
  • JS程序

    摘要:設(shè)計模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計模式必須要先搞懂面向?qū)ο缶幊蹋駝t只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學習總結(jié)。知識只有分享才有存在的意義。 是時候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...

    melody_lql 評論0 收藏0
  • JavaScript設(shè)計模式

    摘要:可能因為先入為主,在編程之中,往往不由自主地以的邏輯編程思路設(shè)計模式進行開發(fā)。這是原型模式很重要的一條原則。關(guān)于閉包與內(nèi)存泄露的問題,請移步原型模式閉包與高階函數(shù)應該可以說是設(shè)計模式的基礎(chǔ)要領(lǐng)吧。在下一章,再分享一下的幾種常用設(shè)計模式。 前 在學習使用Javascript之前,我的程序猿生涯里面僅有接觸的編程語言是C#跟Java——忽略當年在大學補考了N次的C與VB。 從靜態(tài)編程語言,...

    keke 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<