凌晨三点,当启动控制台(console)的时候,手已经按在键盘上了。黑色背景上出现了扎眼的提示,渴求接收命令。想折腾下node.js?node,js有一个令人兴奋的消息:它可以在任何地方运行。这一点让栈有了多种不同的尝试方式。对任何一个经验丰富的人,使用命令行来运行都充满了乐趣。我最喜欢的是可以在命令行的安全网络中审阅栈。牛逼的是我们仍然在说javascript,所以你们中的大部分不会有任何问题。那么,为什么不开始在console里启动node呢?
本文中,我会向你介绍Node.js。我的目标是在一些有难度的地方能看下出彩的部分。本文是一篇概述留存在console里的栈的媒介。如果你想看看适合新手的Node.js指南,不妨看看SitePoint的优质课程Node.js:An Introduction.
为什么是Node.js?
在我们开始前,先看看让Node.js鹤立鸡群的”花边“新闻:
- 为非阻塞I/O而设计
- 为异步操作而设计
- 驱动Chrome的V8 JavaScript
你可能已经从很多地方知道以上几点了,但是这些到底是什么意思呢?你可以将Node.js看作是一个为javascript提供了大量API的引擎。在传统的同步编程方式中,当你有一个I/O操作时,API在运行下一条指令前会处于等待状态。I/O操作就是,举个例子,读取文件或发出一个网络请求。Node.js不同,它从设计之初就是为了异步操作。在当下的编程环境,这一点有巨大的优势。你还能想起你最后一次因为一个更快的单核处理器而换电脑是什么时候吗?处理器的数量和更快的硬盘更加重要。
接下来的文章中,当你看到>这个提示标志时,意味着需要按enter并输入下一条指令。此外,在运行本文中的代码时,你需要开启CLI,并执行指令node(注:需要先下载并安装node.js),说着就开始吧!
回调(Callbacks)
首先输入下列函数:
- >function add(a,b,callback){var result = a+b;callback(result);}
对网络新手,JavaScript中的callback可能很陌生。这看起来并不是一个典型的OOP方法。在JavaScript中,函数(function)是一个对象,对象可以接受其它对象作为参数。JavaScript并不在乎对象是什么,所以一个函数可以接受另一个是函数的对象。数字参数数量,在回调函数中通过add()由两个变为一个。回调系统相当的强大,因为它支持封装并将现实封装隐藏。在Node.js中,你会发现有许多APIs将一个回调作为参数。可以把回调想象成是一个代表。程序隐藏在里面,而这个代表就是经授权被派遣代替别人的人。所以回调像是派一个人来完成差事。给一系列参数,就像列出一个购物单,然后他们就自己去完成任务了。
为了玩转add():
- > add(2, 3, function (c) { console.log(‘2 + 3 = ‘ + c) });
- > add(1, 1, function (c) { console.log(‘Is 1 + 1 = 3? ‘ + (c === 3)); });
多尝试一些回调的新奇玩法吧。它是Node.js中一些重要API的基础。
异步操作
通过回调,我们可以开始编写一些异步API了,举个栗子(其实这个季节似乎没有栗子可以举):
- > function doSomething (asyncCallback) { asyncCallback(); }
- > doSomething(function () { console.log(‘This runs synchronously.‘); });
这个特例中有一个同步操作,但是对于JavaScript中的异步操作,我们已经万事俱备了。asyncCallback,可以在一些线程中被延迟:
- > function doSomething (asyncCallback) { setTimeout(asyncCallback, Math.random() + 1000); }
- > doSomething(function () { console.log(‘This runs asynchronously.‘); }); console.log(‘test‘);
我用了setTimeout在当前线程中延迟操作。timeout并没有给定时间,所以我使用Math.random()来设一个变化的时间。取名为doSomething,然后用console.log(‘test’)来显示被延后的操作。在1~2秒钟后,就可以在屏幕上看懂啊弹出的消息了。我举例的异步回调是不可预测的。Node.js将其放在时间表里,继续前面的进程。时间到了后,Node,js刚好运行到这个异步操作的时间,然后调用这个回调。所以你必须脑中明晰各种回调以更好的理解Node.js。
一句话,在JavaScript中,回调并不总是它看起来的样子。
让我们做一些更有趣的事(原谅我没发现哪里有趣)。不如试试在Node.js中查找DNS?
- > dns.lookup(‘bing.com‘, function (err, address, family) { console.log(‘ Address: ‘ + address + ‘, Family: ‘ + family + ‘, Err: ‘ + err); });
这个回调返回err,address,family三个对象,重点是返回值被当作参数传递给回调。所以这个传统的像var result= fn(‘bing.com‘)不太一样。在Node.js中,必须通过回调和异步来获得总体概况。你可以去看看[DNS Node.js API][2]了解更多细节。下图是我控制台里DNS lookup的样子:
文件I/O
现在继续,在Node,js中怎么执行文件写入写出呢?想象这个场景:你打开一个文件,读取文件内容然后写入新的内容。在现代工艺的电脑上I/O-bound(天,我完全不知道这怎么翻译,知道的私信我,我改)操作很慢。CPU处理速度很快,RAM也很快。但是,硬盘的读取或写入速度很慢。因此当程序同步执行I/O-bound操作时,运行的很慢。替代方法是采用异步,比如:
- > var fs = require(‘fs‘);
- > fs.writeFile(‘message.txt‘, ‘Hello Node.js‘, function () { console.log(‘Saved.‘); }); console.log(‘Writing file...‘);
因为操作是异步的,因此你会先看到“writing file...."然后看到文件被保存。回调函数最普通的使用方式十分适合这个API。你可以看看文档[file System API][6]这部分。读取文件又是怎样做呢?你能通过前例猜猜么?给你一点小提示,回调参数是err和data.我建议你试试。
答案:
- > fs.readFile(‘message.txt‘, function(err, data) { console.log(data); });
你也可以传入一个encoding项来读取文件中utf-8内容:
- > fs.readFile(‘message.txt‘, {encoding: ‘utf-8‘}, function(err, data) { console.log(data); });
Node.js回调函数的异步I/O操作看起来很完美。
Web 服务器
那么在Web服务器上会怎样呢?任何一个优秀的Exposé(MacOS上的多窗口管理工具?)都必须运行一个Web服务端。假设有一个叫做creatServer的API,这个API有一个参数是request和response的回调。你可以在文档中看看HTTP API。你能想到是什么样子吗?你需要Http模块。去console里试试吧。
答案:
- > var http = require(‘http‘);
- > var server = http.createServer(function (request, response) { response.end(‘Hello Node.js‘); });
细想一下web,它是一个带有请求和响应模块的客户端-服务器。Node.js有一个来自客户端的request对象和一个来自服务器的response对象。因此栈通过简单的回调原理解决web难题。还记得它是异步的吗?我希望你能把零散的回忆综合一下,如果你看了API文档,我们现在做的很那个差不多。我们载入一个模块,告诉它要做什么并给它一个参数。回调就会像一个代表那样工作:执行一个有一系列参数的特殊任务。
当然,如果我们不能在浏览器中看到它,那么一切都是无意义的。在CLI中添加:
- server.listen(8080);
然后将你最爱的浏览器指定为:localhost:8080,我用的是Edge。
假设requset对象有一堆可获取的信息。为了重新连上server,先将其关闭(bring it down):
- > server.close();
- > server = http.createServer(function (request, response) { response.end(request.headers[‘user-agent‘]); }); server.listen(8081);
将浏览器指定为:localhost:8081.header提供给你来自浏览器的user-agent信息。我们可以遍历header对象:
- > server.close();
- > server = http.createServer(function (request, response) { Object.keys(request.headers).forEach(function (key) { response.write(key + ‘: ‘ + request.headers[key] + ‘ ‘); }); response.end(); }); server.listen(8082);
这次 将浏览器指定为:localhost:8082。一旦完成之后,记得关闭。否则命令行可能会产生奇怪的运行结果。
- > server.close();
你已经学会啦,通过命令行来创建web服务器。我希望你喜欢这趟node迷幻之旅。
总结
Node.js很适合现代的解决方案,因为其十分简单而轻便。它充分利用了现代硬件非模块设计的优点。它包含了web固有的客户端-服务器模块。最好都用我们喜欢的JavaScript运行。引起我兴趣的是栈的关键部分并不前沿。网页来自早期构建的轻量级易接入模块。如果有时间,我建议你看看Tim Berners-Lees’ Design Principles.。原则中为Node.js的least power applies提供了JavaScript选项。
原文链接:http://www.gbtags.com/gb/share/9522.htm