从零开始学习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注入攻击,这些内容我们以后有机会再展开说吧。