Node.js 基础语法与类型详解

作者:终端旅客时间:2026-10-28 22:36:24浏览:84

本文系统梳理 Node.js 基于 JavaScript 的基础语法,涵盖变量声明、循环语句、运算符特性(如 + 号与 == 区别)、typeof 类型判断及 null/undefined/NaN 特殊值。深入讲解数组与 JSON 对象操作、类(函数)的声明与实例化、成员变量与函数的定义方式,以及匿名函数与回调机制的应用,并附带深拷贝实现示例。

Node.js 基础语法与类型详解

前言

本文旨在梳理 Node.js 中涉及的数据类型知识。参考自网络快照文章,若原作者发现内容有误,请随时联系。

以下内容主要涉及 JavaScript 基础,适合初学者阅读,熟悉者可直接跳过。作者因 JavaScript 基础薄弱,在开发 Node.js 时遇到困惑,故整理此文以巩固知识。

正文

Node.js 的核心基于 JavaScript 脚本语言。与大多数脚本语言一样,JavaScript 具有“弱类型”特征。

与 PHP 不同,PHP 声明新变量无需显式声明类型,而 JavaScript 必须使用 var 关键字。var 在此涵盖了 C++ 中 int、string、char 乃至 function 等所有类型的含义。

本文及后续内容的代码均在 Linux 或 Cygwin 环境下使用 vim 编辑,并在命令行中运行查看结果。其他环境请自行调整操作方式。

基本语法与变量声明

基本语法

变量声明

在 C/C++ 中,变量声明通常如下所示:

```C++

代码如下:

void foo() {}
int a = 0;
char b = 'a';
float c = 1.0f;
void (*d)() = foo; ///< 函数指针,写法可能略有不同

而在 Node.js 中,声明方式如下: ```javascript

代码如下:

function foo() {}
var a = 0;
var b = 'a';
var c = 1.0;
var d = foo;

由此可见,无论变量类型如何,在 Node.js 中均通过 var 关键字进行声明。

循环语句与运算符特性

循环语句

for…i

该循环结构与 C/C++ 类似,基本形式为:

```C++

代码如下:

for(int i = 0; i < foo; i++)
{
//...
}

鉴于 Node.js 的弱类型特性,只需: ```javascript

代码如下:

for(var i = 0; i < foo; i++) {
    //...
}
for…in

这是一种后验型循环语句,类似于 PHP 的 foreach。

例如,我们有一个 JSON 对象如下:

javascript

代码如下:

var foo = {
"hello" : "world",
"node" : "js",
"blahblah" : "bar"
};

此时,我们可以使用 for...in 进行遍历:

javascript

代码如下:

for(var key in foo) {
console.log(key + ": " + foo[key]);
}

如果在命令行执行以下命令:

代码如下:

$ node foo.js

屏幕将显示如下内容:

代码如下:

hello
: world
node: js
blahblah: bar

提示:由上可知,for...in 用于遍历 JSON 对象、数组或对象的键名,不提供直接遍历键值的方式。若需获取键值,必须通过 foo[<当前键名>] 的形式。这与 PHP 的 foreach 存在一定差异。

while…do, do…whill

此处不再赘述,与其他语言无异,仅需注意变量声明时使用 var 即可。

运算符

+, -, *, /

主要运算符包括 +、-、*、/。需特别注意 + 号,它既可用于字符串拼接,也可用于数值运算。弱类型语言中,数字有时以字符串形态出现,字符串有时以数值形态出现,但在必要时仍需明确类型。可通过以下代码观察结果:

代码如下:

var a = "1";
var b = 2;
console.log(a + b);
console.log(parseInt(a) + b);

这里的 parseInt 是 Node.js 的内置函数,用于将字符串解析为 int 类型变量。

上述代码执行结果为:

代码如下:

12
3

注:第一个 console.log 结果为 12,因 a 为字符串,b 亦被转换为字符串进行拼接,结果为字符串连接。第二个 console.log 结果为 3,因将 a 转换为 int 类型后,两 int 变量相加为数值加法,结果为 3。

==, ===, !=, !==

需解释的是,当逻辑运算符长度为 2 时(==, !=),仅判断外在值是否相等,不判断类型。如

代码如下:

var a = 1, b = "1";
console.log(a == b);

其输出结果为 true。若在判断中增加一个等号(===),则为严格判断,需类型和值均相等时才返回 true,否则为 false。即

代码如下:

var a = 1, b = "1";
console.log(a === b);

时,返回结果为 false,因为 a 为 int 型,而 b 为字符串。

顺带提及条件语句,if 与其他语言无异,主要区别在于逻辑运算符中两个等号与三个等号的用法,此处不再赘述。

类型判断与特殊值处理

typeof

此处姑且将其视为运算符而非函数。

该运算符用于判断变量类型,返回类型名字符串。具体执行以下代码即可知晓:

代码如下:

function foo() {}
var a = 0;
var b = '嘘~蛋花汤在睡觉。';
var c = 1.0;
var d = foo;
var e = { "a" : a };
var f = [ 1, 2, 3 ];
var g = null;
var h = undefined;
console.log(typeof a);
console.log(typeof b);
console.log(typeof c);
console.log(typeof d);
console.log(typeof e);
console.log(typeof f);
console.log(typeof g);
console.log(typeof h);

执行结果如下:

代码如下:

number
string
number
function
object
object
object
undefined

null, undefined, NaN

在 JavaScript 中,有三个特殊值,如标题所示。其中第一个大家可能较熟悉,C/C++ 中也有,但为大写,其本质即为

```C++

define NULL 0

而在 JavaScript 中,这三个值的含义各不相同。

### null ###

null 是一种特殊的 object,大致意为空。例如:

var a = null; 大家都能看懂,此处不再赘述。但与 C/C++ 不同的是,此处的 null 与 0 不相等。

### undefined ###

该值表示变量未声明。为更好区分 null,样例代码如下:

```javascript

代码如下:

var a = {
"foo" : null
};
console.log(a["foo"]);
console.log(a["bar"]);

上述代码中,a["foo"] 的值为空,即 null。而 a["bar"] 压根未声明,它连空都不是。输出结果大致可猜:

代码如下:

null
undefined

NaN

这是一个空的数值,是一种特殊的 number。其全称为 Not a Number。略显奇怪,可理解为“不是数字形态”或“数值出错的 number 类型变量”。

多出现在浮点型数值运算错误(如被 0 除)的情况下,甚至可以是用户自行让变量等于 NaN 以返回错误值,提示函数运算出错。

小杂碎

其余语句与其他语言类似,如 break、switch、continue 等,此处不再赘述。

变量类型与数组操作

变量类型

本节主要讲解 JavaScript 对象,其他类型略过。

基础类型

Node.js 包含的基础类型大致如下:

number string boolean array 其中前三种类型可直接赋值,而 array 的赋值仅为引用赋值。在新变量中修改某个值时,旧变量的值也会改变。可尝试以下代码:

javascript var foo = [ 1, 2, 3 ]; var bar = foo; bar[0] = 3; console.log(foo); 其结果为:

javascript [ 3, 2, 3 ] 即 array 若要复制出新数组,不能直接赋值,而必须进行“深拷贝”。

此处有必要讲解 array 的三种创建方法。

第一种:

javascript

代码如下:

var dog = new Array();
dog[0] = "嘘~";
dog[1] = "蛋花汤";
dog[2] = "在睡觉";

第二种:

javascript

代码如下:

var dog = new Array( "嘘~", "蛋花汤", "在睡觉" );

第四种:

javascript

代码如下:

var dog = [
"嘘~",
"蛋花汤",
"在睡觉"
];

个人偏好第三种写法,较为简洁。

JSON对象与类的基础

JSON对象

此处将 JSON 对象单独列出,而非归类为 JavaScript 对象。若觉有误,可直接跳过本节。

本人对 JSON 对象与 JavaScript 对象的区分在于:是否仅用于存储数据,而非类的实例化。JSON 的本质即为 JavaScript Object Notation。

更多有关 JSON 的信息请自行查阅百科。

在 Node.js 中声明一个 JSON 对象非常简单:

javascript

代码如下:

var dog = {
"pre" : "嘘~",
"sub" : {
"name" : "蛋花汤",
"act" : "在睡觉",
"time" : 12
    },
"suf" : [ "我说了", "它在睡觉", "就是在睡觉" ]
};

有两种方式能得到 JSON 对象中某键名的键值,第一种是用点连接,第二种是用中括号:

javascript

代码如下:

dog
.pre;
dog["pre"];

注意:上面在用点的时候,后面直接跟的是 JSON 中的 key。若把 key 当成变量去访问,只能用 dog[key] 试试看。现在自己动手试试,用 for...in 形式遍历上述 JSON 对象。别忘了使用 typeof 喵~

类(对象)的基础

严格意义上,Node.js 的类不能算是类,它只是一个函数集合体,加上一些成员变量。其本质是一个函数。

为通俗讲解,后续内容均称其为“类”,实例化后的称为“对象”。

因类具有许多函数特性,或本质即为函数,故此处顺带讲解函数基础。

类的声明、实例化与成员变量

类的声明和实例化

声明一个类非常简单,请勿嘲笑:

javascript function foo() { //... } 如此,我们便写好了一个 foo 类。

真的假的?!真的。

不信?请继续输入代码验证:

javascript var bar = new foo(); 尽管它是函数,但以这种形式(new)写出,即为该类的实例化。

此处的 foo() 即为该 foo() 类的构造函数。

成员变量

成员变量有两种声明方法。

第一种是在类的构造函数或任何构造函数中使用 this.<变量名>。可在任何时候声明成员变量,即使未声明时调用,也会有 undefined 支撑。即第一种方法:

代码如下:

javascript
function foo() {
this.hello = "world";
}

注意:仅当加上 this 时,才是调用类的成员变量,否则仅为函数内的局部变量。需分清有无 this 时变量的作用范围。

第二种方法是在构造函数或任何成员函数外部声明,格式为 <类名>.prototype.<变量名>:

javascript

代码如下:

function foo() {
    //...
}
foo.prototype.hello = "world";

无论上述哪种方法,均为对成员变量的声明。可查看效果:

javascript

代码如下:

var bar = new foo();
console.log(bar.hello);

甚至可如此修改该类:

javascript

代码如下:

function foo() {
this.hello = "world";
}
foo.prototype.hello = "蛋花汤";

再使用上述代码输出。

思考为何输出仍为 world 而非 蛋花汤。

构造函数与成员函数声明

构造函数

前文已述,foo() 实为构造函数。显然可给构造函数传参,故有如下代码:

javascript

代码如下:

// 代码2.1
function foo(hello) {
if(hello === undefined) {
this.hello = "world";
} else {
this.hello = hello;
    }
}

上述代码中有一个看似奇葩的判断 if(hello === undefined),其作用为何?第一种可能,开发者特意传入 undefined,无可厚非。

另一种情况。前文已述,JavaScript 是弱类型语言,传参亦不严谨。可多传或少传(只要保证程序或逻辑不出错),原则上均允许。多传的参数会被自动忽略,少传的参数则以 undefined 补足。

看以下代码即可明白:

javascript

代码如下:

// 上接代码2.1
var bar1 = new foo();
var bar2 = new foo("蛋花汤");

请自行输出两个 bar 的 hello 变量,会发现一个为 world,一个为 蛋花汤。显而易见,第一个 bar1 在声明时,被 Node.js 自动视为:

javascript

代码如下:

var bar1 = new foo(undefined);

故有 world 一说。

此外,构造函数中传入的参数名为 hello,而类中已有成员变量 this.hello。前文已述,有 this 和无 this 时作用域不同,参数仅作用于构造函数中,而加 this 的则为成员变量。用一个 this 即可区分,故即使同名亦无妨。

成员函数

成员函数声明

成员函数声明与成员变量第二种声明方法类似,即 <类名>.prototype.<函数名> = <函数>;。

javascript

代码如下:

// 上接代码2.1
function setHello(hello) {
this.hello = hello;
}
foo.prototype.setHello = setHello;
bar1.setHello("鸡蛋饼");

上述代码显而易见,实现了 foo 类的 setHello 函数,可通过它修改 foo.hello 的值。

但如此写是否麻烦?接下来要讲 JavaScript 函数的一个重要特性。

匿名函数与回调机制

★ 匿名函数 ★

很多时候,某些函数仅在一个地方被引用或调用,为此函数起名不值,故可临时写好该函数,直接让引用者引用,调用者调用。因此函数可省略函数名,如:

javascript

代码如下:

function(hello) {
this.hello = hello;
}

如何引用或调用呢?若需上述类引用,则写法如下:

javascript

代码如下:

foo.prototype.setHello = function(hello) {
this.hello = hello;
}

此写法与成员函数声明效果相同,且节省大量代码。实际上,基本上的类成员函数声明均采用这种匿名函数方式。

至于如何让匿名函数被调用?这通常用于传入仅被某个函数调用的函数时这样写。

例如,我们有一个函数原型为:

javascript

代码如下:

/**
* 我们将传入a,b两个变量,
* 在算出a+b的值后,交由func(num)
* 去进行输出
 */
function sumab(a, b, func) {
var c = a + b;
func(a, b, c);
}

例如,我们有两个版本的输出函数,一个是中文输出,一个是英文输出。若不使用匿名函数,写法如下:

javascript

代码如下:

function zh(a, b, sum) {
console.log(a + " + " + b + " 的值是:" + sum);
}
function en(a, b, sum) {
console.log(a + " plus " + b + " is " + sum);
}
sumab(1, 2, zh);
sumab(3, 4, en);

执行这段代码,输出结果将为:

1 + 2 的值是:3 3 plus 4 is 7 若采用匿名函数形式,则写法如下:

javascript

代码如下:

sumab(1, 2, function(a, b, sum) {
console.log(a + " + " + b + " 的值是:" + sum);
});
sumab(3, 4, function(a, b, sum) {
console.log(a + " plus " + b + " is " + sum);
});

这种形式通常用于回调函数。回调机制算是 Node.js 或 JavaScript 的精髓,将在后续篇章介绍。

成员函数声明的匿名函数声明方式

虽上一节已讲,此处再复述一遍。

通常声明类成员函数时,均使用匿名函数声明,因该函数仅是类的一个成员函数,不会在其他地方单独引用或调用,故有如下代码:

javascript

代码如下:

// 上接代码2.1
foo.prototype.setHello = function(hello) {
this.hello = hello;
}

如此,便使 foo 类拥有了 setHello 函数。

类的随意性与深拷贝实现

2.3.4. 类的随意性

此为胡扯。所谓类的随意性,即 JavaScript 中可在任何地方修改类,这与 Ruby 有一定相似之处。

例如,string 本身也是一个类,拥有 length 等成员变量,以及 indexOf、substr 等成员函数。若觉得 string 某处不完善,想添加自己的方法,可在所需处为其增加函数,例如:

javascript

代码如下:

String.prototype.sb = function() {
var newstr = "";
for(var i = 0; i < this.length; i++) {
if(i % 2 === 0) newstr += "s";
else newstr += "b";
  }
return newstr;
};

该函数意为填充字符串,使其变成 sb 的化身。

我们来测试一下:

代码如下:

var str = "嘘~蛋花汤在睡觉。";
console.log(str.sb());

你将得到如下结果:

sbsbsbsbs 你跟你的电脑说“嘘~蛋花汤在睡觉。”,你的电脑会骂你四次半傻逼。(赶快砸了它)

3. 附

3.1. 深拷贝

所谓深拷贝,即新建一个数组或对象,将源数组或对象中的基础类型变量值逐个手动拷贝过去,而非仅获取源数组或对象的引用。这涉及递归调用等。

以下是我实现的一个深拷贝函数,大家可以写一个自己的,并加入 Node.js 知识库中。

javascript

代码如下:

function cloneObject(src) {
var dest = {};
for(var key in src) {
if(typeof src === "object") dest[key] = cloneObject(src[key]);
else dest[key] = src[key];
  }
return dest;
}
作者最新文章