从Javascript到Typescript到Node.js

最近看了点typescript的东西,加上以前看过的一点点Node.js,所以就想把他们系统地整理一下。

Javascript

这玩意搞过Web开发的应该都知道吧,Javascript的语法我就不废话了,挺简单的。这里总结几个Javascript的核心机制部分吧。

关于对象

Javascript里所有东西都是对象,数字是Number,数组是Array,字符串是String,函数也是Function对象。而所有对象都基于Object对象。

闭包

闭包在Javascript里是很重要的概念,很多实现机制都会利用这点。闭包由function确立,子闭包可以访问外部的变量,但是外部的不能访问字闭包内的成员。特别是涉及回掉函数的地方,可以利用这一点来避免透传参数。

原型模型

Javascript的一个重要特点就是它是原型模型的(什么是原型模型请参考《设计模式》)。function是一个原型为Function实例的对象,同时可以由它来创建新的对象。在Javascript对象查找成员的时候,如果当前对象不存在,就会去其原型中查找。按照原型链找到头为止。如果还没找到,就返回undefined。 每一个function都可以通过设置prototype成员指定它的类型原型实例,要注意这里是原型的实例而不是原型的类型。比如

a.prototype = new b();

然后在new这个function的时候,新对象的原型就回指向这个原型实例。 另外,由于Javascript没有类似protected和private之类的关键字。所以在模拟面向对象继承的时候,只能使用私有成员(通过var申明)公有成员(使用this.[成员名字]或[function名称].[成员名字]申明)

Javascript的坑

作为一个脚本语言,果断是要有坑滴。且不说弱类型语言在复杂项目中类型混用的问题。单就boolean类型就有一个小坑。

比较符号

和大多数脚本语言一样,javascript是可以隐式类型转换滴,而且Number类型的0和字符串类型的空串转换成boolean的时候都是false。所以如果出现 0 == false 和 “” == false 之类的比较,返回的都是true。如果要加类型判断要用 0 === false 和 “” === false 这样,这就返回false了,对应的不等号是!==,和PHP一样。

eval

eval函数的作用闭包是当前闭包,所以执行完

eval('var abc = 123;');

之后,abc就变成Number类型的值为123的东东了,后面也可以用到。 忽略var关键字 有的时候会忽略或者忘记掉var关键字,比如:

var a = b = 123;

这行代码会把b写到window对象(浏览器)或global对象(Node.js)中。 同样,一不注意可能写出这样的代码

for(key in list){
...
}

同样这会把key带出,导致一些未预料的错误,所以应该补全var关键字

for(var key in list){
...
}

工具

Javascript部分的最后记录一点Javascript和网页相关的工具。

Typescript

要使用Javascript做大型的东西还是有不少坑的。首先它是弱类型的,一旦项目庞大了就很难保证类型不出错。并且有时候不经意间就会用到浏览器特有的东西。而Typescript就可以用来帮助你约束一些脚本行为,并编译成兼容各种浏览器的Javascript代码。 Typescript由微软开发,提供了Visual studio编译插件、Web版转换器(话说这个在线IDE真心给力)和Node.js插件,向上兼容Javascript语法。

类型限定

Typescript的特点之一是增加了类型限定。比如:string, number, boolean, any等等。在定义变量的时候,可以在后面加 : [类型名]来指定类型。如果发现类型不匹配或者出现非法的隐式类型转换就会编译报错。如:

var a: number;
var b: string;
var c: any;

为了向上兼容Javascript,所以如果不声明类型的话,默认是any。有一个特殊的类型,函数。函数类型的申明有点像lambda表达式。比如:

var a: (Number, string) => string

表示a是一个函数,第一个参数类型是Number,第二个是string,返回值是string类型。 另外,数组类型就是在普通类型后面加方括号[],如:

var a: string[];

变量限定

除了对类型进行规范以外,typescript还可以对未申明变量进行检查,避免前面说到的忘记写var的问题。 在typescript中,如果直接使用未定义的变量,会编译错误。而如果想要申明外部变量,可以用declare关键字,比如:declare var jQuery;

接口Interface

在typescript中,可以用interface关键字来申明接口。而使用这个接口的地方,编译程序会检查传入的变量和常量的类型是不是包含接口中声明的全部功能,并且类型一致。

模块Module

module关键字用于声明模块,其实就是Javascript里的闭包啦,需要注意的是module里的默认的东西都是private的,也就是用var申明的,如果要把它变成public的要在前面加export,比如:

export function getEnvironment() {
    return new TEnvironment();
};

export interface IEnvBrowserInfo {
    getBrowserName: () => string;
    getBrowserVersion: () => TEnvVersionInfo;
    getBrowserKernelName: () => string;
    isCompatMode: () => bool;
    isCookieEnabled: () => bool;
 };

类class

Typescript增加了class关键字,用于定义一个类。类里面的constructor方法作为构造函数。 对于typescript的类,仅支持单继承和private与public关键字。不支持protected。其内部privatevar实现,public由[类名|this].[成员名实现]。而继承呢,就是前面说的prototype实现的。但是可以继承多个interface,因为interface只是一个类型限定而已。 在构造函数里,可以使用super关键字访问父类的成员,而且这个关键字也只能在构造函数里使用。

其他特性其他特性像什么common.js和AMD的推荐和define Property要求第三方组件和高版本js引擎就略过啦。前两个是异步加载公共模块的东东,后一个是模仿C#的set和get的玩意。

Node.js

Node.js是用于服务端的Javascript开发框架。Javascript部分基于Google V8引擎,据说性能非常之不错。 它采用了Javascript回调那种优雅的设计模式,最初是为了解决高连接数下的服务器性能消耗问题。并且其申称不会用锁,所以不会有死锁。 Node.js的作者实现了一些底层系统操作,并交付到javascript接口使用。比如进程、网络、加解密、内存管理、文件系统等。还实现了global对象替代浏览器里的window对象,作为最外层的闭包使用。 Node.js的功能都是按模块划分的,具体模块可以见 http://nodejs.org/api/ 。而且Typescript可以作为Node.js的插件供其使用。

用Node.js实现网络服务十分简单,比如官方的HTTP服务器例子:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

C++

如果要把Node.js和C++结合起来也比较easy(至少我觉得比lua简单多了,可能是因为它设计上就是面向对象的)。要把C++代码和Node.js打通,就需要Google V8引擎、Node.js的node::ObjectWrap包装基类。所有要导出到Javascript的类都需要继承node::ObjectWrap

所有要导出的C++模块都要实现一个初始化函数,初始化函数的申明式为void Initialize (Handle exports); 的形式。然后要通过Node.js的一个宏NODE_MODULE([模块名称], [初始化函数])来定义模块。 然后在初始化函数中通过 exports->Set(String::NewSymbol("符号名称"), 符号内容); 来注册函数或者类符号。 之后要建立node-gyp的编译配置文件bind.gpy

{
  "targets": [
    {
      "target_name": "编译目标文件名",
      "sources": [ "源码文件集合" ]
    }
  ]
}

然后执行 node-gyp configure build 编译就好了。编译完后默认会放在build/Release/addon里。然后node.js里用js代码require进来就好

var addon = require('./build/Release/[编译目标名]);
console.log( 'call:', addon.[注册的符号调用] );

这样,基本的Node.js插件的开发就完成了,具体可以参照:http://nodejs.org/api/addons.html

Debug

Node.js调试起来也比较Nice,大致上3种方式

  • 第一种是类似gdb的调试方法,直接 node debug [javascript文件名],后面的操作类似gdb

  • 第二种是Google为V8引擎做的Eclipse插件,具体使用方法可以参考这里 http://cnodejs.org/blog/?p=911 ,然后用node –debug-brk[=端口号] [javascript文件名] 来启动远程调试。

  • 第三种是直接拿Chrome浏览器来调试,这个比较Nice。首先要通过npm install -g node-inspector安装node-inspector扩展;再启动Node.js程序并加–debug或者–debug-brk选项;最后,浏览器端的使用方式类似Android设备Chrome的远程调试,地址栏里输入http://[IP地址]:[ node-inspector端口号,默认8080]/debug?port=[Node.js的Debug选项的端口号] 来连接调试服务,就完啦。

最后

其他Node.js的功能就不介绍了,都是些功能模块,需要用到就在如然后用呗,官方文档挺简单易懂的。主要是Node.js利用了Javascript里伪多线程的全异步的设计思路。然后借由无锁操作来让开发人员可以快速地开发比较高效的服务程序。

Last updated