Node.js开发者必须熟悉的四个JavaScript概念-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

Node.js开发者必须熟悉的四个JavaScript概念

简介: 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/50054087 Node.js开发者必须熟悉的四个JavaScript概念 作者:chszs,未经博主允许不得转载。
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/50054087

Node.js开发者必须熟悉的四个JavaScript概念

作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs

Node.js是一个服务器端的开发框架,它基于Google Chrome的V8 JavaScript引擎构建。尽管Node.js自身是使用C++开发的,但是它使用JavaScript作为其应用语言。
Node.js有四个概念对于初学者非常重要,应该理解并掌握它们。如下:

一、非阻塞或异步I/O

由于Node.js是一个服务器端框架,因此它的主要工作之一就是处理来自浏览器的请求。在传统的I/O系统中,只有先前请求的响应返回来后,新的请求才能发出。这也就是为什么称之为阻塞I/O的通信。服务器阻塞了下一个到来的请求,然后处理当前的请求,直到请求处理完成,发出响应,再解除下一个到来的请求的阻塞。

Node.js不遵循上面的阻塞I/O通信原则。如果一个请求需要的处理时间较长,Node.js会把请求发送到事件循环中,然后在调用栈上处理下一个请求。一旦事件循环中的请求完成了处理,它会通知Node.js,Node.js会返回响应给浏览器。下面可以看一个例子:

1、阻塞式I/O

// 餐桌1,获取订单1
var order1 = orderBlocking(['Coke', 'Iced Tea']);
// 服务订单1
serveOrder(order1);
// 一旦订单服务完成,服务员去另一张餐桌
// 餐桌2,订单2
var order2 = orderBlocking(['Coke', 'Water']);
// 服务订单2
serveOrder(order2);
// 一旦订单服务完成,服务员去另一张餐桌
// 餐桌3,订单3
var order3 = orderBlocking(['Iced Tea', 'Water']);
// 服务订单3
serveOrder(order3);
// 一旦订单服务完成,服务员去另一张餐桌

上面的例子中,服务员在第一个餐桌获得订单,然后向订单提供服务,服务完成后,服务员立刻移动到下一张餐桌获得订单。订单是按时间顺序进行处理的,服务器仅仅是服务于订单和阻塞其它的订单。

2、非阻塞式I/O

// 在餐桌1取走交付的订单并移动到下一个餐桌
orderNonBlocking(['Coke', 'Iced Tea'], function(drinks){
  return serveOrder(drinks);
});
// 在餐桌2取走交付的订单并移动到下一个餐桌
orderNonBlocking(['Beer', 'Whiskey'], function(drinks){
  return serveOrder(drinks);
});
//在餐桌3取走交付的订单并移动到下一个餐桌
orderNonBlocking(['Hamburger', 'Pizza'], function(food){
  return serveOrder(food);
});

在上面的例子中,服务员去获取订单并通知厨师,再去下一个餐桌。在第一个订单被处理期间,服务员移动到下一个餐桌去获取订单,服务员不阻塞订单。

二、原型

在JavaScript中,原型即Prototype,是一个比较复杂的概念。Node.js使用了原型的地方很多,因此每一个JavaScript开发者都应该熟悉这个概念。

像Java、C++等编程语言都实现了典型的继承,这有助于代码的重用。首先构建一个基类(作为对象的蓝图),然后从这个类创建对象或扩展这个类。

但是JavaScript语言没有这样的概念。首先在JavaScript中创建一个对象,然后扩展这个对象或者从这个对象中创建新的对象。这就是所谓的原型继承,它通过原型来实现。

每一个JavaScript对象都链接到一个原型对象,并且可以从原型对象中继承其属性。原型有点类似于面向对象语言中的类,但实际上是不同的,它们自身都是对象。每一个对象都链接到Object.prototype,它是JavaScript预定义的对象。

如果你在通过obj.propName或obj[‘propName’]来查看属性时,这个对象有没有这样的属性,可以通过obj.hasOwnProperty(‘propName’)来检查,JavaScript的运行时会查看原型对象中是否有这个属性。如果原型对象没有这样的属性,然后再依次检查此对象本身有没有这样的属性(有可能对象继承了几级),直到匹配到此属性。如果整个属性链都没有这样的属性,那么就会返回未定义的值。

用例子来说明这一点:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
var otherPerson = Object.create(person);

当创建了一个新对象时,你可以选择这个对象的原型。在上面的代码中,我们增加了一个Object函数的create方法。create方法创建了一个新对象,并且使用了另一个对象作为它的原型,并作为参数传递到新对象。

当我们修改了新对象,它的原型还保留原样,不受影响。但是如果我们要修改原型对象,那么就会影响到所有基于此原型对象的对象。

原型是一个很复杂的概念,需要继续深入。

三、模块

如果你熟悉Java语言的包Package概念,那么你会理解Node.js的模块这个概念,两者没什么不同。模块是简单的JavaScript文件,它包含了特定目的的代码。模块模式(Module Pattern)用于简化代码的组织和导航。要使用模块的属性,你必须在JavaScript中require导入它,与Java语言中的import导入相同。

Node.js中有两者类型的模块:

1、核心模块(Core Module)

核心模块包含了预编译到Node.js的库。核心模块的模板是向开发者提供经常出现和重复的代码片段,如果没有这些,那么开发者会陷入这些大量重复且冗长无趣的工作中。常见的核心模块包括:HTTP模块、URL模块、EVENTS模块、文件系统模块等。

2、用户定义的模块(User Defined Module)

用户定义的模块是开发者为了特定功能自己实现的模块。通常是核心模块不能满足所需的功能时开发的。用户定义的模块同样需要require导入。如果是核心模块,require导入只需传入模块名,而对于用户定义的模块,require导入还需要传入文件系统的路径。

比如:

// 导入核心模块
var http = require('http);
// 导入用户定义的模块
var something = require('./folder1/folder2/folder3/something.js');

四、回调

在JavaScript语言中,函数被认为是第一级的对象。这意味着开发者可以把函数当作是常规对象那样,做所有的操作。可以把函数赋值给一个变量,还可以把函数作为参数传递给方法,还可以把函数作为对象的属性,甚至可以从函数返回函数。

回调是JavaScript语言中的一种匿名函数,它可以作为参数传递给另一个函数,还可以在随后的函数执行中执行回调函数或返回回调函数。这是回调函数广泛使用的编程范式。

把回调函数作为参数传递给另一个函数时,我们只需传递函数定义,也即,我们无需知道回调函数什么时候得以执行。这完全取决于调用函数的机制,故以回调命名。回调函数是Node.js的非阻塞通信和异步处理的基础。

setTimeout(function() {
    console.log("world");
}, 2000)
console.log("hello");

这是最简单的回调函数的例子之一,我们把匿名函数当作参数传递给setTimeout函数,而匿名函数仅仅是在控制台输出日志“world”。由于这只是函数定义,我们并不知道它什么时候得以执行,执行取决于setTimeout函数的2000毫秒后输出。

故第二个日志语句先在控制台输出“hello”,然后等两秒后再输出回调函数定义的日志“world”。

// output
hello
world

理解以上四个概念有助于深入Node.js。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: