從零開始學習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
屬性 - 修改
innerText
或textContent
屬性
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注入攻擊,這些內容我們以后有機會再展開說吧。