好程序員技術文檔HTML5開發中的javascript閉包

  好程序員技術文檔HTML5開發中的javascript閉包,事實上,通過使用閉包,我們可以做很多事情。比如模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率,同時避免對命名空間的污染,最重要的是可以從一個域中取出原本訪問不到的變量去使用。

  函數的作用域:

  1.變量的聲明會提前。

  2.全局變量和局部變量。

  3.函數作用域的生命周期。

  變量的聲明

  tt='ee';

  function b(){

  function test(){

  alert(tt);

  var tt='ff';

  }

  test()

  }

  b()

  第一段函數返回的是一個undefined, 因為變量的聲明會提前相當于

  var tt='123'

  function test(){

  var tt;

  alert(tt);

  tt='ff';

  }

  2.

  tt='ee';

  function b(){

  function test(){

  alert(tt);

  /var tt='ff';/

  }

  test()

  }

  b()

  變量作用域

  1.function outFn(){

  function innerFn(){

  var inner=0;

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  //每當這個內部函數被調用的時候,都會重新生命一個inner變量,然后讓這個變量自增。

  2.

  var inner=0;

  function outFn(){

  function innerFn(){

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  //每當內部函數被調用時,都會讓全局變量inner自增。

  如果讓這個函數變成父元素的局部變量,會是什么結果?

  3.

  function outFn(){

  var inner=0;

  function innerFn(){

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  一段概念:

  這次的結果可能會出人預料,因為當內部函數在定義它的作用域的外部被引用的時候,就創建了改內部函數的一個閉包,這種情況下我們稱既不是局部變量也不是其參數的變量為自由變量,稱內部函數的調用環境為封閉閉包的環境。從本質上將,如果內部函數引用了位于外部函數中的變量,相當于授權該變量能夠被延遲使用。因此,當外部函數調用完成后,這些變量的內存不會被釋放,因為閉包仍然需要使用它們。

  閉包

  一句話理解閉包: JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里。

  一個故事理解閉包

  我的年齡是秘密,你想知道。

  但是每次我都含糊其辭的對你說 undefined;

  為了防止我自己也忘記或搞錯自己的年齡,我辦了一張身份證,上面記錄我的年齡信息,藏在我家里。

  你知道了這件事,為了得到我的年齡,決定對我投其所好,

  于是你送我一只逗比間諜貓。

  作為感謝我給了你一把我家的鑰匙,方便你有空來看貓。

  這只貓實在太神奇了,每次都能找到我的身份證,并把信息傳遞給你。

  于是你每次想知道我的年齡的時候就來看貓,

  然后間諜貓每次都能把我的最新的年齡信息反饋給你。

  var day=0;

  var timer=setInterval("dayChanges()",(246060*1000)); //定時器,

  function dayChanges(){

  day++;

  } //每過24小時 次函數運行一次,我的年齡又多了一天

  //------------ 下面的代碼就是一個比較常見的閉包。

  function isMyHome(){ //我家

  var myAge=0; //我的身份證信息

  if(day%365==0){ //我的年齡變化

  myAge+=1;

  }

  function myHomeKey(){ // 我家鑰匙

  return myAge; //return 就是間諜貓

  }

  return myHomeKey; //給你我家的鑰匙。

  }

  var key=isMyHome(); //你拿到我家鑰匙

  var you=key(); //得到年齡。

  匿名函數:

  (functoin( ){

  alert( 2 )

  })()

  特性:1.自調用

  2.參數傳入方式;

  閉包的使用方法:

  1.

  function outFn(){

  console.log('我是外部函數');

  function innerFn(){

  console.log('我是內部函數');

  }

  innerFn()

  }

  outFn();

  //可以在外部函數調用到內部函數,但是并不能在外部調用內部函數。這種技術適合用于小型,單用途的函數。

  2.在任何地方調用內部函數,通過一個全局變量可以訪問到內部函數;

  var global;

  function outFn(){

  console.log('我是外部函數')

  function innerFn(){

  console.log('我是內部函數')

  }

  global=innerFn;

  }

  outFn();

  global();

  3.一個最簡單的閉包,可以迅速調用,并且不會污染全局變量。

  var data = {

  table : [1,2,3,4,5],

  tree : {}

  };

  (function(data){

  alert(data.table)

  //build dm.tree

  })(data);

  4.可以通過對變量進行封裝實現閉包。

  var obj=(function(){

  var name='找不見的名字';

  return {

  getName:function(){

  return name;

  },

  setName:function(newName){

  return newName

  }

  }

  })()

  obj.getName()

  高階函數英文叫Higher-order function。那么什么是高階函數?

  JavaScript的函數其實都指向某個變量。既然變量可以指向函數,函數的參數能接收變量,那么一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。

  一個最簡單的高階函數:

  function add(x, y, f) {

  return f(x) + f(y);

  }

  當我們調用add(-5, 6, Math.abs)時,參數x,y和f分別接收-5,6和函數Math.abs,根據函數定義,我們可以推導計算過程為:

  x = -5;

  y = 6;

  f = Math.abs;

  f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;

  return 11;

  用代碼驗證一下:

  add(-5, 6, Math.abs); // 11

  編寫高階函數,就是讓函數的參數能夠接收別的函數

  1. 高階函數map

  舉例說明,比如我們有一個函數f(x)=x2,要把這個函數作用在一個數組[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map實現如下:

  由于map()方法定義在JavaScript的Array中,我們調用Array的map()方法,傳入我們自己的函數,就得到了一個新的Array作為結果:

  function pow(x) {

  return x * x;

  }

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

  map()傳入的參數是pow,即函數對象本身。

  你可能會想,不需要map(),寫一個循環,也可以計算出結果:

  var f = function (x) {

  return x * x;

  };

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  var result = [];

  for (var i=0; i

  result.push(f(arr[i]));

  }

  的確可以,但是,從上面的循環代碼,我們無法一眼看明白“把f(x)作用在Array的每一個元素并把結果生成一個新的Array”。

  所以,map()作為高階函數,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意復雜的函數,比如,把Array的所有數字轉為字符串:

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

  只需要一行代碼。

  2. 高階函數reduce

  再看reduce的用法。Array的reduce()把一個函數作用在這個Array的[x1, x2, x3...]上,這個函數必須接收兩個參數,reduce()把結果繼續和序列的下一個元素做累積計算,其效果就是:

  [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

  比方說對一個Array求和,就可以用reduce實現:

  var arr = [1, 3, 5, 7, 9];

  arr.reduce(function (x, y) {

  return x + y;

  }); // 25

  練習:利用reduce()求積。

  練習:把[1, 3, 5, 7, 9]變換成整數13579,用reduce()。

  練習:請用map()把用戶輸入的不規范的英文名字,變為首字母大寫,其他小寫的規范名字。輸入:['adam', 'LISA', 'barT'],輸出:['Adam', 'Lisa', 'Bart']。

  3. 高階函數filter

  filter也是一個常用的操作,它用于把Array的某些元素過濾掉,然后返回剩下的元素。

  和map()類似,Array的filter()也接收一個函數。和map()不同的是,filter()把傳入的函數依次作用于每個元素,然后根據返回值是true還是false決定保留還是丟棄該元素。

  例如,在一個Array中,刪掉偶數,只保留奇數,可以這么寫:

  var arr = [1, 2, 4, 5, 6, 9, 10, 15];

  var r = arr.filter(function (x) {

  return x % 2 !== 0;

  });

  r; // [1, 5, 9, 15]

  把一個Array中的空字符串刪掉,可以這么寫:

  var arr = ['A', '', 'B', null, undefined, 'C', ' '];

  var r = arr.filter(function (s) {

  return s && s.trim(); // 注意:IE9以下的版本沒有trim()方法

  });

  r; // ['A', 'B', 'C']

  可見用filter()這個高階函數,關鍵在于正確實現一個“篩選”函數。

  練習:請使用filter()篩選出一個數組中所有的素數

  函數作為返回值

  高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。

  我們來實現一個對Array的求和。通常情況下,求和的函數是這樣定義的:

  function sum(arr) {

  return arr.reduce(function (x, y) {

  return x + y;

  });

  }

  sum([1, 2, 3, 4, 5]); // 15

  但是,如果不需要立刻求和,而是在后面的代碼中,根據需要再計算怎么辦?可以不返回求和的結果,而是返回求和的函數!

  function lazy_sum(arr) {

  var sum = function () {

  return arr.reduce(function (x, y) {

  return x + y;

  });

  }

  return sum;

  }

  當我們調用lazy_sum()時,返回的并不是求和結果,而是求和函數:

  var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()

  調用函數f時,才真正計算求和的結果:

  f(); // 15

  在這個例子中,我們在函數lazy_sum中又定義了函數sum,并且,內部函數sum可以引用外部函數lazy_sum的參數和局部變量,當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中,這種稱為“閉包(Closure)”的程序結構擁有極大的威力。

  請再注意一點,當我們調用lazy_sum()時,每次調用都會返回一個新的函數,即使傳入相同的參數:

  var f1 = lazy_sum([1, 2, 3, 4, 5]);

  var f2 = lazy_sum([1, 2, 3, 4, 5]);

  f1 === f2; // false

  f1()和f2()的調用結果互不影響。

  閉包

  注意到返回的函數在其定義內部引用了局部變量arr,所以,當一個函數返回了一個函數后,其內部的局部變量還被新函數引用,所以,閉包用起來簡單,實現起來可不容易。

  還有一個需要注意的問題是,返回的函數并沒有立刻執行,而是直到調用了f()才執行。我們來看一個例子:

  function count() {

  var arr = [];

  for (var i=1; i<=3; i++) {

  arr.push(function () {

  return i * i;

  });

  }

  return arr;

  }

  var results = count();

  var f1 = results[0];

  var f2 = results[1];

  var f3 = results[2];

  在上面的例子中,每次循環,都創建了一個新的函數,然后,把創建的3個函數都添加到一個Array中返回了。

  你可能認為調用f1(),f2()和f3()結果應該是1,4,9,但實際結果是:

  f1(); // 16

  f2(); // 16

  f3(); // 16

  全部都是16!原因就在于返回的函數引用了變量i,但它并非立刻執行。等到3個函數都返回時,它們所引用的變量i已經變成了4,因此最終結果為16。

  返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者后續會發生變化的變量。

  如果一定要引用循環變量怎么辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量后續如何更改,已綁定到函數參數的值不變:

  function count() {

  var arr = [];

  for (var i=1; i<=3; i++) {

  arr.push((function (n) {

  return function () {

  return n * n;

  }

  })(i));

  }

  return arr;

  }

  var results = count();

  var f1 = results[0];

  var f2 = results[1];

  var f3 = results[2];

  f1(); // 1

  f2(); // 4

  f3(); // 9

  注意這里用了一個“創建一個匿名函數并立刻執行”的語法:

  (function (x) {

  return x * x;

  })(3); // 9

  理論上講,創建一個匿名函數并立刻執行可以這么寫:

  function (x) { return x * x } (3);

  但是由于JavaScript語法解析的問題,會報SyntaxError錯誤,因此需要用括號把整個函數定義括起來:

  (function (x) { return x * x }) (3);

  通常,一個立即執行的匿名函數可以把函數體拆開,一般這么寫:

  (function (x) {

  return x * x;

  })(3);

  說了這么多,難道閉包就是為了返回一個函數然后延遲執行嗎?

  當然不是!閉包有非常強大的功能。舉個栗子:

  在面向對象的程序設計語言里,比如Java和C++,要在對象內部封裝一個私有變量,可以用private修飾一個成員變量。

  在沒有class機制,只有函數的語言里,借助閉包,同樣可以封裝一個私有變量。我們用JavaScript創建一個計數器:

  'use strict';

  function create_counter(initial) {

  var x = initial || 0;

  return {

  inc: function () {

  x += 1;

  return x;

  }

  }

  }

  它用起來像這樣:

  var c1 = create_counter();

  c1.inc(); // 1

  c1.inc(); // 2

  c1.inc(); // 3

  var c2 = create_counter(10);

  c2.inc(); // 11

  c2.inc(); // 12

  c2.inc(); // 13

  在返回的對象中,實現了一個閉包,該閉包攜帶了局部變量x,并且,從外部代碼根本無法訪問到變量x。換句話說,閉包就是攜帶狀態的函數,并且它的狀態可以完全對外隱藏起來。

  閉包還可以把多參數的函數變成單參數的函數。例如,要計算xy可以用Math.pow(x, y)函數,不過考慮到經常計算x2或x3,我們可以利用閉包創建新的函數pow2和pow3:

  function make_pow(n) {

  return function (x) {

  return Math.pow(x, n);

  }

  }

  // 創建兩個新函數:

  var pow2 = make_pow(2);

  var pow3 = make_pow(3);

  pow2(5); // 25

  pow3(7); // 343

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279760.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279760.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279760.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

亞馬遜echo中國使用_如何使用亞馬遜的主要照片備份所有照片

亞馬遜echo中國使用Millions of people are Amazon Prime subscribers, but many of them don’t realize that in addition to free shipping and Prime Instant Video, they also get unlimited photo storage for all their computers and mobile devices. 數以百萬計的人是…

抽象SQL查詢:SQL-MAP技術的使用

什么是參數化查詢&#xff1f;我們來看百度百科對此的定義和示例&#xff1a; 一&#xff0c;定義 ------------------------------------------------------------------ 參數化查詢&#xff08;Parameterized Query 或 Parameterized Statement&#xff09;是指在設計與數據庫…

EF ORM

//新增UserInfo userInfo new UserInfo();userInfo.UserName "YANG";userInfo.UserPass "123";userInfo.Email "253qq.com";userInfo.RegTime System.DateTime.Now;Model1Container db new Model1Container();db.UserInfoSet.Add(userInfo…

如何使用echo.js實現圖片的懶加載(整理)

如何使用echo.js實現圖片的懶加載&#xff08;整理&#xff09; 一、總結 一句話總結&#xff1a;a、在img標簽中添加data-echo屬性加載真實圖片&#xff1a;<img class"loading" src"blank.gif" data-echo"圖片真實路徑" />&#xff0c;在…

還原出廠設置 擦除frp_如何備份,擦除和還原Apple Watch

還原出廠設置 擦除frpThe Apple Watch is, in its own right, a little tiny computer with data backup and security needs. Read on as we show you how to ensure your Apple Watch is backed up, wiped, and restored just like you’d do with your smartphone. Apple Wa…

exchange 2010 search mailbox 的幕后強大功能

鈴……….半夜中被一陣急促的手機鈴聲吵醒&#xff0c;年度服務客戶打來電話需要進行郵件的排查和刪除工作。問其原因&#xff0c;原來是組織中有人發了一封關于領導的不健康的郵件&#xff0c;并在企業內部進行了轉發&#xff0c;領導要求立即找出此類郵件并進行刪除。管理員深…

Apache HBase的現狀和發展

一、&#xff28;Base是什么 HBase(Hadoop Database)&#xff0c;是一個基于Google BigTable論文設計的高可靠性、高性能、可伸縮的分布式存儲系統。 它有以下特征&#xff1a; 1.HBase仍然是采用行存儲的&#xff0c;采用松散表的結構來獲得動態列的功能&#xff1b; 2.原生海…

Java的接口、繼承與多態

接口 java只支持單繼承&#xff0c;即一個類只能有一個父類&#xff0c;因此需要接口來實現多重繼承。 接口的定義 類和接口的區別&#xff1a;一個類通過繼承接口的方式&#xff0c;從而來繼承接口的抽象方法。類描述對象的屬性和方法&#xff0c;接口則包含類要實現的方法。 …

dvd刻錄軟件_如何在Windows 7中刻錄照片和視頻DVD(無需額外的軟件)

dvd刻錄軟件Software like DVD Flick is great for burning video to DVDs, but Windows 7 actually includes built-in DVD burning software. Strangely, it’s the last time the company did so—while Windows 8 and Windows 10 can play back DVD movies, they can’t cr…

如何實現office不同語言界面切換

前面我討論了《如何實現win7不同語言界面切換》&#xff0c;很巧今天又有網友問到如何實現 office的語言界面切換呢。嘿&#xff0c;那我們就繼續來玩轉界面吧。 office2007和office2010也支持輕松的進行語言界面切換&#xff0c;操作步驟也很簡單。 Office 語言界面包 (LIP) 是…

Mysql-高可用集群[MyCat中間件使用](三)

服務器-節點: 4臺 mysql-主: 192.168.2.40mysql-從-node-0: 192.168.2.41mysql-從-node-1: 192.168.2.42mycat: 192.168.2.45操作過程 1.搭建mysql主從節點2.搭建mycat中間件節點3.mycat服務配置4.測試讀寫分離,讀的分發1.搭建mysql主從節點 Mysql-高可用集群主從單一模式-binl…

yum安裝mysql5.6

1.檢查系統是否安裝其他版本的MYSQL數據 yum list installed | grep mysql yum -y remove mysql-libs.x86_64 2.安裝及配置 wget http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm rpm -ivh mysql-community-release-el6-5.noarch.rpm yum repolist all | grep…

離開互聯網行業_如何使用互聯網再也不會離開家

離開互聯網行業Thanks to the Internet, activities like “going outside” or “being a productive member of the community” are becoming increasingly optional parts of daily life. When your inner hermit feels like putting on his vampire cape, simple tricks l…

iOS 11開發教程(十三)iOS11應用編輯界面添加視圖

iOS 11開發教程&#xff08;十三&#xff09;iOS11應用編輯界面添加視圖 在iOS中添加視圖的方式有兩種&#xff1a;一種是使用編輯界面添加視圖&#xff1b;另一種是使用代碼添加視圖。以下是這兩個方式的詳細介紹。 1.編輯界面添加視圖 使用編輯界面添加視圖是一個相當簡單的工…

限流算法(記錄cyc大佬的專欄)

限流的必要性 如果一段時間內請求的數量過大&#xff0c;就會給服務器造成很大壓力&#xff0c;可能導致服務器無法提供其它服務。 計數器算法 通過一個計數器 counter 來統計一段時間內請求的數量&#xff0c;并且在指定的時間之后重置計數器。該方法實現簡單&#xff0c;但是…

bzoj 1024 [ SCOI 2009 ] 生日快樂 —— 遞歸

題目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id1024 因為每次把一塊切成兩塊&#xff0c;所以可以枚舉從哪里切開&#xff0c;然后遞歸求解&#xff1b; 一開始用了不太對的貪心思路&#xff0c;想著一定去切較長邊&#xff0c;但看來不一定。 代碼如下&a…

HBase存儲剖析與數據遷移

1.概述 HBase的存儲結構和關系型數據庫不一樣&#xff0c;HBase面向半結構化數據進行存儲。所以&#xff0c;對于結構化的SQL語言查詢&#xff0c;HBase自身并沒有接口支持。在大數據應用中&#xff0c;雖然也有SQL查詢引擎可以查詢HBase&#xff0c;比如Phoenix、Drill這類。但…

windows os x_如何立即在OS X上獲取Windows樣式的窗口捕捉

windows os xApple’s recent announcement that the upcoming OS X release (El Capitan or 10.11) will finally, at long last, come with the ability to snap windows to your screen edges. A feature Windows users have enjoyed since 2009. 蘋果公司最近宣布即將發布的…

Install Odoo 11 on CentOS 7

2019獨角獸企業重金招聘Python工程師標準>>> Odoo is the most popular all-in-one business software in the world. It offers a range of business applications including CRM, website, e-Commerce, billing, accounting, manufacturing, warehouse, project m…

創建maven項目,配置maven地址

2019獨角獸企業重金招聘Python工程師標準>>> 在eclipse中&#xff0c;新建maven項目next第二步&#xff0c;選擇webapp輸入id及包名&#xff0c;完成新建給項目build path&#xff0c;添加1.8版本jdk和tomcat項目右鍵屬性 project Facets&#xff0c;切換成web項目&…