Node.js是一个让javascript可以运行在服务器端的平台,是一个为数据密集型(data-intensive),实时Web(Real-time Web)应用开发而诞生的平台,它让java-script成为脚本语言世界的一等公民。传统意义上,javascript由DOM,BOM(浏览器对象模型),ECMAscript组成,而Mozilla则指出javascript由Core javascript和Client javascript组成。可以认为,Node.js中所谓的javascript只是Core javascript。
Node.js最大的特点就是采用异步式I/O和事件驱动的架构。与异步式相对的同步式I/O(即阻塞I/O),同步式I/O在面对I/O操作(磁盘读写,网络通信等的总称)时,一个线程只能处理一项任务,因为同步式I/O采用被称为阻塞的线程调度方式(即面对I/O操作时,操作系统会剥夺该线程对cpu的控制权,在被剥夺控制权的这段时间内,该线程是被“阻塞”的),因而产生了多线程,但同时多线程又带来了频繁的上下文切换。而Node.js采用的是非阻塞I/O(即异步式I/O),不存在同步式I/O存在的问题,也就不需要多线程,但为了处理异步I/O,线程就必须有事件循环。
Node.js中的大多数API都是异步的,虽然有些也提供了同步版本,对我们开发者而言,我们可以这样理解Node.js中的异步API,在执行时,这些API只是将I/O请求(以回调函数的形式)发给操作系统后,继续执行后面的代码,等待代码都执行完后, 再进入事件煦循环监听事件。例:
var fs = require('fs');fs.readFile('file.txt', function(er, data){
if(er) {
console.log(er + '3333333333');
} else {
console.log(data);
}
})console.log(1);
fs.readFile('file.txt', function(er, data){
if(er) {
console.log(er + '4444444444');
} else {
console.log(data);
}
})console.log(2);
运行结果如下:
1
2
Error: ENOENT, open 'file.txt'3333333333
Error: ENOENT, open 'file.txt'4444444444
javascript高级特性
作用域(scope)
不像c,c++,javascript的作用域不是以花括号包围的块级作用域(block scope),它的作用域是通过函数来定义的。javascript的函数定义是可以嵌套的,每一层是一个作用域,变量搜索顺序是从内到外的。例:
var scope = 'global';function f1() {
console.log(scope);
}f1();//输出global
var scope = 'global';function f1() {
console.log(scope);
var scope = 'f';
}f1();//输出undefined
从上边的例子中我们可以看出,在第二个例子里,在console.log
处,程序发现scope变量在函数里有定义,便不在搜索函数外的作用域,但是在console.log
处,scope变量并没有初始化,因而结果是undefined
。
javascript的作用域是静态作用域(词法作用域),函数作用域的嵌套关系是定义时决定的,即作用域的嵌套关系可以在语法分析时确定,而不必等到运行时决定。例:
var scope = 'top';function f1() {
console.log(scope);
}f1(); //输出 top;
function f2() {
var scope = 'f2';
f1();
}f2(); //输出 top;
闭包(closure)
闭包的严格定义“由函数(环境)及封闭的自由变量组成的集合体。”
所谓闭包的特性,是一个函数返回它内部定义的一个函数时,就产生了一个闭包,闭包不但包括被返回的函数,还包括这个函数定义的环境。例(counter和generatecounter()的局部变量就是一个闭包):
var generatecounter = function() {
var counter = 0;
var get = function() {
counter ++;
}
return get;
}var counter1 = generatecounter();
var counter2 = generatecounter();
console.log(counter1());//1
console.log(counter2());//1
console.log(counter1());//2
console.log(counter2());//2
console.log(counter2());//3
Node.js中闭包的应用:嵌套的回调函数。
对象
javascript中的对象实际上是是一个由属性组成的关联数组,属性由名称和值组成,值的类型可以是任何数据类型,或者函数和其他对象。javascript真的没有类,只有对象,对象就是对象,不是类的实例,javascript的对象是基于原型的。
创建和访问
//对象的创建
//方法一
var foo = {};
foo.prop_1 = 'bar';
foo.prop_2 = false;
foo.prop_3 = function() {
return 'Hello world!';
}
console.log(foo.prop_3());
//方法2
var foo = {};
foo['prop_1'] = 'bar';
foo['prop_2'] = false;
foo['prop_3'] = function() {
return 'Hello world!';
}
//方法3(用对象的初始化器)
var foo = {
'prop_1' : 'bar',
prop_2 : false, //对象中属性名称是否加引号是可选的,除非名称中有空格或者其他可能造成歧义的字符
//否则没必要加引号。
prop_3 : function() {
return 'Hello world!';
}
};
//方法4(构造函数:与前三中方法相比不是一次性的)
function User(name, uri) {
this.xingming = name;
this.email = uri;
this.display = function () {
console.log('Hello world!');
}
}
var user = new User('wa', '[email protected]');
console.log(user.xingming);
上下文对象
在javascript中,上下文对象就是this指针,即被调用函数所处的环境。上下文对象的作用是在一个函数内部引用调用它的对象本身,javascript的任何函数都是被某个对像调用的,包括全局对象,所以this指针是一个非常重要的东西。在使用不同的引用调用同一个函数,this指针永远是这个引用所属的对象。本质上,函数类型的变量是指向这个函数实体的一个引用,在引用之间赋值不会对对象产生复制行为。我们可以通过函数的任何一个引用调用这个函数,不同之处仅仅在于上下文。
apply和call方法:功能是以不同的对象作为上下文来调用某个函数。例:
function a(b,c) {
console.log(this.a == 'c');
console.log(this.b == 'b');
console.log(b == 'first');
console.log(c == 'second');
}a.call({a : 'c'}, 'first', 'second');//true false true true 以参数表来接受被调函数的参数
a.apply({b : 'b'}, ['first', 'second'])//false true true true 以数组来接受被调函数的参数