從零開始學習javascript項目(0)——匿名函數的自調用,閉包

從零開始學習javascript項目(0)

目標:實現一個實時的數據采集系統,并把采集到的數據反饋出來

需要的相關知識

  • 匿名自調函數的使用
  • 操作DOM
  • 事件監聽

匿名自調函數

這個問題其實應該分成兩個獨立的問題來看。分別是匿名函數和自調用。

匿名函數

顧名思義,匿名函數就是沒有聲明函數名的函數就像下面這樣

setTimeout(function () { 
    // some code...
}, 1000);

如果不想聲明匿名函數,使這個函數被調用我們可以這樣

方式1

setTimeout(function xx () {
  // some code...
}, 1000);

或者這樣

方式2

var xx = function () {
  // some code...
};

setTimeout(something, 1000);

這兩種聲明方式的區別在于方式1聲明的函數的作用域在括號內部,只能被這個函數本身遞歸調用,而方式2聲明的作用域在最外部的作用域。也是我們經常用的一種方式。

函數的自調用

所謂的自調用,就是在定義函數的時候直接使用這個函數

就像下面這樣

var xx = function (x){return x+1}(3);

這段代碼的本質上就是下面這樣

var xx = function (x){
  return x + 1;
}

var result = xx(3);

這樣寫可以更省事一點

下面我們稍微深入一點,說一下返回函數的函數

返回函數的函數

在javascript機制里面,一切都是對象,函數也是一個對象,所以,函數可以作為一個返回值被傳遞出去

就像下面這樣

var xx = function (){
  var result = function (x){
    return x + 1;
  } 
  return result;
}

var fuck = xx()(3);
// 這里可以等價于
//var x = xx();
//var fuck = x(3);

這里有要注意的是

  • 在函數里面返回result 那里因為返回的是一個函數而不是這個函數運算的值,所以不能加括號

同樣的,這段代碼里面的x的位置我們可以稍微換一下

var xx = function (x){
  var result = function (){
    return x + 1;
  } 
  return result;
}

var fuck = xx(3)();
// 這里可以等價于
//var x = xx(3);
//var fuck = x();

把x的位置放到前面效果也是一樣的。

在這里面函數在第一次調用的時候只是把x這個變量保存起來而沒有進行計算。如果我們稍微再往前走一步。定義兩個變量。就像下面這樣

var xx = function (a){
  return function (b){
    return a + b;
  }
}

var fuck = xx(10)(9);

在這里我們構建的xx函數就可以分別收集兩個參量,這兩個參量是相互獨立的。就像這樣

var xx = function (a){
  return function (b){
    return a + b;
  }
}

var fuck = xx(10); // 在這里我們設置的a的值
fuck(9);           //在這里我們補充了b的值

我們可以利用這一特性完成一對字符串的配對工作。就像下面這樣

function xx(a){
  return function(b){
    if(a + b === "En Taro Tassadar") return true;
    else return false;
  };
}   
var fuck ;
(function(){
  var aa = 'En Taro T';
  fuck = xx(aa);
}());

(function(){
  var bb = 'assadar';
  fuck = xx(bb);
}());

fuck;

閉包

現在我們順便提一下閉包。閉包這個概念在perl里面也有。閉包本質上就是利用函數作用域的相關特性,實現把函數內部和外部分割開來的一種方式。就像上面的例子中我們把校驗一段字符串的時候不需要同時知道兩段字符串的值。再舉一個簡單一點的例子

function xx(){
  var a = 0;
  return function(){
    a += 1;
    return a;
  };
}

var fuck = xx();
fuck(); // => 1
fuck(); // => 2
fuck(); // => 3
fuck(); // => 4

在這里我們就調用了一個不會被編譯器優化的變量a,這個變量會一直保存在內存當中。如果你做過嵌入式編程,那么這個段代碼的效果和c里面聲明變量時候的const關鍵字的意義有點像。缺點也很像,都比較占內存。嗯,就是這樣。

自執行

當你看到這里時,我只能假設你已經對作用域(Scope)和執行上下文(Context)有了一定的了解。上下文通常取決于函數是怎么樣被調用的。可能在調用一個函數的時候,這個函數所使用的變量只存在于這個函數的上層函數當中。這個上下文提供了一種簡單的辦法來創建自由變量或者私有函數。

// 由于該function里返回了另外一個function,其中這個function可以訪問自由變量i
// 所有說,這個內部的function實際上是有權限可以調用內部的對象。

function makeCounter() {
    // 只能在makeCounter內部訪問i
    var i = 0;

    return function () {
        console.log(++i);
    };
}

// 注意,counter和counter2是不同的實例,分別有自己范圍內的i。

var counter = makeCounter();
counter(); // logs: 1
counter(); // logs: 2

var counter2 = makeCounter();
counter2(); // logs: 1
counter2(); // logs: 2

alert(i); // 引用錯誤:i沒有defind(因為i是存在于makeCounter內部)。

下面來說一下什么是自調用(Immediately-Invoked Function Expression)。也就是立即執行的函數表達式(IIFE)。當我們在聲明函數以后,在后面添加添加一個括號就可以讓這個函數立刻執行起來。

如果我們這樣寫

function(){ /* code */ }();

那么編譯器會提示你SyntaxError: Unexpected token (

如果我們這樣寫

var xx = function(){ /* code */ };
xx();

那么就沒問題了。

如果你這樣寫

function xx(){ /* code */ }( 1 );

這回不報錯了,但是人家也不會執行。在一個表達式后面添加括號,這個表達式會立刻執行,但是在一個語句后面加括號 ,那個是一個分組操作符。所以上面的代碼等效于

function xx(){ /* code */ }
( 1 );

所以,在做這種自執行函數的時候,只要用大括號把整個括起來就行了。就像下面這樣

// 下面2個括弧()都會立即執行

(function () { /* code */ } ()); // 推薦使用這個
(function () { /* code */ })(); // 但是這個也是可以用的

// 由于括弧()和JS的&&,異或,逗號等操作符是在函數表達式和函數聲明上消除歧義的
// 所以一旦解析器知道其中一個已經是表達式了,其它的也都默認為表達式了
// 不過,請注意下一章節的內容解釋

var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();

// 如果你不在意返回值,或者不怕難以閱讀
// 你甚至可以在function前面加一元操作符號

!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

// 還有一個情況,使用new關鍵字,也可以用,但我不確定它的效率
// http://twitter.com/kuvos/status/18209252090847232

new function () { /* code */ }
new function () { /* code */ } () // 如果需要傳遞參數,只需要加上括弧()

DOM

如果我們想實現需求中的目標,那么我們至少應該有下面三個步驟

+------------------------+
|    get form context    |
+-----------+------------+
            |
+-----------v-------------+
|     click button to     |
|    triger the event     |
+-----------+-------------+
            |
+-----------v-------------+
|  display on the screen  |
+-------------------------+

(PS.工具來自[這里][http://asciiflow.com/]。挺不好用的,不是閑的蛋疼不要嘗試)

首先,我們需要利用javascript去操作html文本中的信息,包括但是不限于(獲取文本框中的數據,修改html頁面內容)。這里就需要引入DOM這個概念了。HTML文檔會被瀏覽器解析成一顆DOM樹,于是,我們可以利用javascript來操作DOM。

DOM可以被進行的操作類型

  • 更新更新:更新該DOM節點的內容,相當于更新了該DOM節點表示的HTML的內容;
  • 遍歷:遍歷該DOM節點下的子節點,以便進行進一步操作;
  • 添加:在該DOM節點下新增一個子節點,相當于動態增加了一個HTML節點;
  • 刪除:將該節點從HTML中刪除,相當于刪掉了該DOM節點的內容以及它包含的所有子節點。

DOM的選取

document.getElementById()
document.getElementsByTagName()
document.getElementsByClassName()

DOM 更新

  • 修改innerHTML屬性
  • 修改innerTexttextContent屬性

DOM插入

一種方法是使用appendChild關鍵字把一個子節點添加到父節點的最后一個子節點

<!-- HTML結構 -->
<p id="js">JavaScript</p>
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>
var list = document.getElementById('list');
var haskell = document.creatElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell'
list.appendChild(haskell);

于是我們就可以得到下面的

<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="haskell">Haskell</p>
</div>

另一種方法是在制定位置之前插入子節點parentElement.insertBefore(newElement, referenceElement);子節點會被插入到referenceElement的位置之前。

DOM刪除

在父節點調用removeChild即可

HTML中的事件監聽

使用addEventListener可以實現這一點

<p>該實例使用 addEventListener() 方法來向按鈕添加點擊事件。</p>
<button id="myBtn">點我</button>
<p id="demo"></p>
<script>
document.getElementById("myBtn").addEventListener("click", function()
{
    document.getElementById("demo").innerHTML = "Hello World";
});
</script>

代碼實現

在掌握上面三點知識以后,我們可以在此基礎上實現這個小項目了。

html部分的代碼

我們需要

  • 一個輸入框
  • 一個提交按鈕
  • 返回數據的標簽
<label>數據輸入:<input id="input" type="text"> <button id="button">確認填寫</button></label>
<div>
    您的輸入值是:<span id="display">尚未錄入</span>
</div>

現在html部分差不多就這樣了

javascript部分的代碼

我們需要

  • 一個能自執行的匿名函數
  • 能夠抓取DOM中的數據
  • 向DOM中寫入數據
  • 能對一個點擊事件發生反饋
//首先我們操作DOM去獲得三個節點的值
  var node_in = document.getElementById("input");
  var node_btn = document.getElementById("button");
  var node_display = document.getElementById("display");
//然后為click添加一個事件監聽
node_btn.addEventListener("click",click,false);
//最后是一個事件監聽函數
  node_btn.addEventListener("click",click,false);
  function click(){
      var val = node_in.value.replace( /^(\s|\u00A0)+|(\s|\u00A0)+$/g, "" );
      if(val.length > 0){
          node_display.textContent = val
      }
      else{
          alert("Please complete the form!");
      }
  }

到這里,一個簡單的任務就完成了,但是有很多知識還是值得深究的。比如正則表達式的使用、關于IE瀏覽器的兼容、以及防止XSS注入攻擊,這些內容我們以后有機會再展開說吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • 前言 歸根結底,代碼都是思想和概念的體現。沒人能把一種程序設計語言的所有語法和關鍵字都記住,可以查閱參考書來解決。...
    朱細細閱讀 2,956評論 4 14
  • 情景一: warmth在做數學練習題,做完后拿給爸爸檢查。爸爸看完很快就發現了孩子的問題,爸爸用心的和孩子講解,發...
    溫暖媽咪閱讀 452評論 8 5
  • 靜安,新閘路,沁園邨,這爿老房子,二十多年前第一趟去那兒的同學家,是哪一年,雖都忘得模糊不堪,卻還記得——一個夏天...
    一頁閱讀 252評論 0 1
  • 朋友H有一天突然給我打電話,急切而神秘地說:“W你認識吧!你知道嗎!他現在是某某數字出版機構的出版主編!那家公司最...
    何初見閱讀 405評論 0 1