NodeJs基本语法和类型
写在前面
今天想要查下Node的类型什么的知识,想要总结下,在Googol上看到一个文章,但是原始的链接不在了,在快照中把这篇文章拉出来,如果原作者有问题,请联系我!
该文章都是一些JS的基础,高手自动跳过!我之前没怎么写过js,这方面比较弱,所以在写node的时候也遇到了麻烦,这里给自己补充下知识!
正文
Node.js的基础是JavaScript这门脚本语言。而大多数的脚本语言一个共同的特点就是“弱类型”。
不同于PHP的是,PHP就是是有了新变量也无需申明,而JavaScript则还是需要var来申明一下的。而这个var涵盖了C++中的int、string、char等一切类型的含义,甚至是function。
本篇以及后篇的所有内容都是在Linux或者Cygwin下用vim进行编辑(若不是则请自行转变成你自己的方法),然后在命令行下进行查看结果的。
基本语法
变量声明
在C/C++中,我们这么声明变量的:
```C++
voidfoo(){} inta=0; charb='a'; floatc=1.0f; void(*d)()=foo;///<忘了是不是这么写的了,总之是函数指针
而在Node.js中则是这样的:
```javascript
functionfoo(){} vara=0; varb='a'; varc=1.0; vard=foo;
所以,无论是什么类型的变量,在Node.js中都是以一个var来解决的。
循环语句
for…i
这个循环语句基本上跟C/C++一样,都是
```C++
for(inti=0;i<foo;i++) { //... }
而鉴于Node.js是弱类型,所以只需要:
```javascript
for(vari=0;i<foo;i++){ //... } for…in
这是一种后有型的循环语句,类似于PHP的foreach。
比如我们有一个JSON对象如下:
javascript
varfoo={ "hello" :"world", "node" :"js", "blahblah" :"bar" };
这个时候我们就可以用for...in来循环遍历了:
javascript
for(varkeyinfoo){ console.log(key+":"+foo[key]); }
我们如果在命令行中打入下面的命令:
$nodefoo.js
屏幕上就会显示下面的内容了:
hello :world node:js blahblah:bar
提示:由上可知,for...in语句是用来遍历JSON对象、数组、对象的键名的,而不提供键值的遍历。如果要获取键值,只能通过foo[<当前键名>]的形式来获取。这个跟PHP的foreach还是有一定区别的。
while…do,do…whill
这个就不多做解释了,跟其它语言没什么大的区别,无非就是如果有变量声明的话,需要用var就够了。
运算符
+,-,*,/
这几个运算符也就这样,要注意的是+。它既可以作用于字符串,也可以作用于数值运算。弱类型语言虽然说类型是弱的,数字有时候可以以字符串的形态出现,字符串有时候可以用数值的形态出现,但是在必要的时候也还是要说一下它是什么类型的,我们可以用下面的代码去看看结果:
vara="1"; varb=2; console.log(a+b); console.log(parseInt(a)+b);
这里的parseInt是Node.js的一个内置函数,作用是将一个字符串解析成int类型的变量。
上面的代码执行结果是:
12 3
注:第一个console.log结果是12,由于a是字符串,所以b也被系统以字符串的姿态进行加操作,结果就是将两个字符串黏连在一起就变成了12。而第二个console.log结果是3,是因为我们将第一个a转变为了int类型,两个int型的变量相加即数值相加,结果当然就是3了。
==,===,!=,!==
这里有一点要解释,当这个逻辑运算符长度为2的时候(==,!=),只是判断外在的值是不是一样的,而不会判断类型。如
vara=1,b="1"; console.log(a==b);
它输出的结果就是true。但是如果我们在中间判断的时候再加上一个等号,那么就是严格判断了,需要类型和值都一样的时候才会是true,否则就是false。也就是说
vara=1,b="1"; console.log(a===b);
的时候,返回的结果就是false了,因为a是int型的,而b则是字符串。
顺带着就把条件语句讲了吧,其实这里的if跟别的语言没什么两样,就是几个逻辑运算符两个等号三个等号的问题。所以就不多做累述了。
typeof
这里我姑且把它当成是一个运算符而不是函数了。
这个运算符的作用是判断一个变量的类型,会返回一个字符串,即类型名,具体的执行下面的代码就知道了:
functionfoo(){} vara=0; varb='嘘~蛋花汤在睡觉。'; varc=1.0; vard=foo; vare={"a":a}; varf=[1,2,3]; varg=null; varh=undefined; console.log(typeofa); console.log(typeofb); console.log(typeofc); console.log(typeofd); console.log(typeofe); console.log(typeoff); console.log(typeofg); console.log(typeofh);
这里的执行结果就将会是:
number string number function object object object undefined
null,undefined,NaN
在JavaScript中,有三个特殊的值,如标题所示。其中第一个大家可能都比较熟悉吧,C/C++里面也有,不过是大写的,其本质就是一个
```C++
defineNULL0
而在JavaScript中,这三个值所代表的意义都不同。
###null###
null是一种特殊的object,大致的意思就是空。比如说:
vara=null;
大家都能看懂,就不多做解释了。但是跟C/C++不同的是,这个null跟0不相等。
###undefined###
这个东西的意思就是说这个变量未声明。为了能够更好地区分null,我们的样例代码如下写:
```javascript
vara={ "foo":null }; console.log(a["foo"]); console.log(a["bar"]);
上面的代码中,我们让a["foo"]的值为空,即null。而压根没有声明a["bar"]这个东西,它连空都不是。输出的结果大家都差不多应该猜到了:
null undefined
NaN
这是一个空的数值,是一个特殊的number。它的全称是NotaNumber。有点奇怪,大家可以理解为不是数字形态,或者数值出错的number类型变量。
多在浮点型数值运算错误(如被0除)的情况下出现,甚至可以是用户自己让一个变量等于NaN以便返回一个错误值让大家知道这个函数运算出错了云云。
小杂碎
其它剩余的语句也跟已存在的其它语言差不多,比如说break啊、switch啊、continue啊等等等等。
变量类型
这一节主要讲的是JavaScript对象,其它类型差不多一带而过吧。
基础类型
Node.js包含的基础类型差不多有如下几个:
number
string
boolean
array
其中前三种类型可以直接赋值,而array的赋值只是一个引用赋值而已,在新变量中改变某个值的话旧变量的值也会改变,直接可以试试下面的代码:
javascript
varfoo=[1,2,3];
varbar=foo;
bar[0]=3;
console.log(foo);
它得出的结果是:
javascript
[3,2,3]
也就是说array要是复制出一个新的数组的话,不能用直接赋值的方法,而必须“深拷贝”。
这里有必要讲一下array的三种创建方法。
第一种:
javascript
vardog=newArray(); dog[0]="嘘~"; dog[1]="蛋花汤"; dog[2]="在睡觉";
第二种:
javascript
vardog=newArray("嘘~","蛋花汤","在睡觉");
第四种:
javascript
vardog=[ "嘘~", "蛋花汤", "在睡觉" ];
我个人比较喜欢第三种写法,比较简洁。
JSON对象
这里我把JSON对象单独拎出来而不是把它归类为JavaScript对象,如果觉得我有点误人子弟就可以直接跳过这一节了。
本人对于JSON对象和JavaScript对象的区分放在是否只用来存储数据,而并非是一个类的实例化。其实JSON的本质便是JavaScriptObjectNotation。
更多有关JSON的信息请自行百科。
在Node.js中声明一个JSON对象非常简单:
javascript
vardog={ "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
functionfoo(){
//...
}
好了,我们已经写好了一个foo类了。
真的假的?!真的。
不信?不信你可以接下去打一段代码看看:
javascript
varbar=newfoo();
别看它是一个函数,如果以这样的形式(new)写出来,它就是这个类的实例化。
而这个所谓的foo()其实就是这个foo()类的构造函数。
成员变量
成员变量有好两种方法。
第一种就是在类的构造函数或者任何构造函数中使用this.<变量名>。你可以在任何时候声明一个成员变量,在外部不影响使用,反正就算在还未声明的时候使用它,也会有一个undefined来撑着。所以说这就是第一种方法:
javascript functionfoo(){ this.hello="world"; }
注意:只有在加了this的时候才是调用类的成员变量,否则只是函数内的一个局部变量而已。要分清楚有没有this的时候变量的作用范围。
第二种方法就是在构造函数或者任何成员函数外部声明,其格式是<类名>.prototype.<变量名>:
javascript
functionfoo(){ //... } foo.prototype.hello="world";
无论上面哪种方法都是对成员变量的声明,我们可以看看效果:
javascript
varbar=newfoo(); console.log(bar.hello);
甚至你可以这么修改这个类:
javascript
functionfoo(){ this.hello="world"; } foo.prototype.hello="蛋花汤";
然后再用上面的代码输出。
想想看为什么输出的还是world而不是蛋花汤。
构造函数
我们之前说过了这个foo()实际上是一个构造函数。那么显然我们可以给构造函数传参数,所以就有了下面的代码:
javascript
//代码2.1 functionfoo(hello){ if(hello===undefined){ this.hello="world"; }else{ this.hello=hello; } }
我们看到上面有一个奇葩的判断if(hello===undefined),这个判断有什么用呢?第一种可能,就是开发者很蛋疼地特意传进去一个undefined进去,这个时候它是undefined无可厚非。
还有一种情况。我们一开始就说了JavaScript是一门弱类型语言,其实不仅仅是弱类型,它的传参数也非常不严谨。你可以多传或者少传(只要保证你多传或者少传的时候可以保证程序不出错,或者逻辑不出错),原则上都是可以的。多传的参数会被自动忽略,而少传的参数会以undefined补足。
看看下面的代码就明白了:
javascript
//上接代码2.1 varbar1=newfoo(); varbar2=newfoo("蛋花汤");
请自行输出一下两个bar的hello变量,会发现一个是world一个是蛋花汤。显而易见,我们的第一个bar1在声明的时候,被Node.js自动看成了:
javascript
varbar1=newfoo(undefined);
所以就有了它是world一说。
还有就是在这个构造函数中,我们看到了传进去的参数是hello而这个类中本来就有个成员变量就是this.hello。不过我们之前说过了有this和没this的时候作用域不同,那个参数只是作用于构造函数中,而加了this的那个则是成员变量。用一个this就马上区分开来他们了,所以即使同名也没关系。
成员函数
成员函数声明
成员函数的声明跟成员变量的第二种声明方法差不多,即<类名>.prototype.<函数名>=<函数>;
javascript
//上接代码2.1 functionsetHello(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) *去进行输出 */ functionsumab(a,b,func){ varc=a+b; func(a,b,c); }
比如我们有两个版本的输出函数,一个是中文输出,一个是英文输出,那么如果不用匿名函数时候是这么写的:
javascript
functionzh(a,b,sum){ console.log(a+"+"+b+"的值是:"+sum); } functionen(a,b,sum){ console.log(a+"plus"+b+"is"+sum); } sumab(1,2,zh); sumab(3,4,en);
执行一遍这段代码,输出的结果将会是:
1+2的值是:3
3plus4is7
这样的代码如果采用匿名函数的形式则将会是:
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(){ varnewstr=""; for(vari=0;i<this.length;i++){ if(i%2===0)newstr+="s"; elsenewstr+="b"; } returnnewstr; };
这个函数的意思就是填充一个字符串,使其变成sb的化身。
我们来测试一下:
varstr="嘘~蛋花汤在睡觉。"; console.log(str.sb());
你将会得到这样的结果:
sbsbsbsbs
你跟你的电脑说“嘘~蛋花汤在睡觉。”,你的电脑会骂你四次半傻逼。(赶快砸了它)
3.附
3.1.深拷贝
所谓深拷贝就是自己新建一个数组或者对象,把源数组或者对象中的基础类型变量值一个个手动拷过去,而不是只把源数组或者对象的引用拿过来。所以这就涉及到了一个递归的调用什么的。
下面是我实现的一个深拷贝函数,大家可以写一个自己的然后加入到自己的Node.js知识库中。
javascript
functioncloneObject(src){ vardest={}; for(varkeyinsrc){ if(typeofsrc==="object")dest[key]=cloneObject(src[key]); elsedest[key]=src[key]; } returndest; }