(四)nodejs循序渐进-函数,类和对象(基础篇)

上一篇文章讲到了基本数据类型和运算符,相信大家都能做简单的运算,本篇文章将讲述函数,类,对象。

函数

在nodejs中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。Node.js 中函数的使用与 JavaScript 类似,举例来说,你可以这样做:

function say(word) {console.log(word);
}function execute(someFunction, value) {someFunction(value);
}execute(say, "Hello");

以上代码中,我们把 say 函数作为 execute 函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!

这样一来, say 就变成了execute 中的本地变量 someFunction ,execute 可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。

匿名函数

很多时候我们的某些函数只在一个地方被引用或者调用,那么我们为这个函数起一个名字就太不值了,没必要,所以我们可以临时写好这个函数,直接让引用它的人引用它,调用它的人调用它。所以函数可以省略函数名。

我们可以把一个函数作为变量传递。但是我们不一定要绕这个"先定义,再传递"的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:

function execute(someFunction, value) {someFunction(value);
}execute(function(word){ console.log(word) }, "Hello");

我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。

用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。

比如我们有两个版本的输出函数,一个是中文输出,一个是英文输出,那么如果不用匿名函数时候是这么写的:

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

这样的代码如果采用匿名函数的形式则将会是:

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);
});

这种形式通常使用于回调函数。回调机制算是 nodejs 的精髓。在以后的篇章会做介绍。

JSON对象

json是我们常用的轻量级数据交换结构,它在这里并不是通过类实例化的对象,所以我这里把它单独提出来。

声明一个 JSON对象 非常简单:

var player = {"user_id"     : "1927u893233c2993u429o34","user_name"   : "高司机","gender"      : "man","age"         : 18,"level"       : 2,"viplevel"    : 0,"exp"         : 23933,"section"     : 12,"achievement" : null,"resetinfo"       : {"dailyreset_time"  : "20201122000000","prayreset_time"   : "20201122030000", }
};console.log("userid:"+player.user_id);
console.log("userid:"+player["user_id"]);

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

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

不过为了通俗地讲,我们接下去以及以后都将其称为“类”,实例化的叫“对象”。

因为类有着很多函数的特性,或者说它的本质就是一个函数,这个需要大家和其他C++,java语言要区别开,不要以为只有用class声明的才是类,不信你看下面:

函数的方式定义类

声明类

这里我用函数的特性来声明一个类

function fun() {console.log("hello world");
}

好了,我们已经写好了一个fun类了。请继续保持怀疑的心态来看这段代码:

var fun1 = new fun();

别看它是一个函数,如果以这样的形式new写出来,它就是这个类的实例化。

而这个所谓的fun()其实就是这个fun()类的构造函数。

成员变量有好两种方法。

第一种就是在类的构造函数或者任何构造函数中使用 this.<变量名> 。你可以在任何时候声明一个成员变量,在外部不影响使用,反正就算在还未声明的时候使用它,也会有一个 undefined 来撑着。所以说这就是第一种方法:

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

注意:只有在加了 this 的时候才是调用类的成员变量,否则只是函数内的一个局部变量而已。要分清楚有没有 this 的时候变量的作用范围。二话不说看代码:

function fun(){this.data = "this.data";var data = "local data";console.log("data:"+this.data);console.log("data:"+data);
}
var fun1 = new fun();

输出什么内容呢,请思考下再看结果。

第二种方法就是在构造函数或者任何成员函数外部声明,prototype 属性使您有能力向对象添加属性和方法。其格式是 <类名>.prototype.<变量名>: 

function fun() { 
}fun.prototype.print = "member value:this.print";
var fun1 = new fun();
console.log(fun1.print);

 

甚至你可以这么修改这个类:

function fun() {this.print = "in fun call print";
}fun.prototype.print = "hello world";
var fun1 = new fun();
console.log(fun1.print);

然后再用上面的代码输出。

但是不如你所愿,你期望输出“hello world”,但是输出的是"in fun call print",不要着急,等看完下面你就会有答案了,如果你还不懂,请在文章下方留言。

构造函数 

我们之前说过了这个fun()实际上是一个构造函数。那么显然我们可以给构造函数传参数,所以就有了下面的代码:

function fun(param) {if(param === undefined) {this.print = "hello wolrd";} else {this.print = param;}
}

我们看到上面有一个奇葩的判断 if(hello === undefined),这个判断有什么用呢?第一种可能,就是开发者很蛋疼地特意传进去一个 undefined 进去,这个时候它是 undefined 无可厚非。

还有一种情况。我们一开始就说了 JavaScript 是一门弱类型语言,其实不仅仅是弱类型,它的传参数也非常不严谨。你可以多传或者少传(只要保证你多传或者少传的时候可以保证程序不出错,或者逻辑不出错),原则上都是可以的。多传的参数会被自动忽略,而少传的参数会以 undefined 补足。

看看下面的代码就明白了:

var fun1 = new fun("hello");
console.log(fun1.print);
var fun2 = new fun();
console.log(fun2.print);

输出结果是:

hello
hello wolrd

显而易见,我们的fun2在声明的时候,被解释成 

var fun2 = new fun(undefined);

 成员函数

成员函数的声明跟成员变量的第二种声明方法差不多,即 <类名>.prototype.<函数名> = <函数>;

function fun(param) {if(param === undefined) {this.print = "hello wolrd";} else {this.print = param;}
}function setPrint(param) {this.print = param;
}
fun.prototype.Print = setPrint;var fun1 = new fun("hello");
fun1.Print("不好意思,我这里写个BUG");
console.log(fun1.print);

上面这段代码显而易见,我们实现了fun类的Print函数,能通过它修改fun.print的值,所以输出结果为:

不好意思,我这里写个BUG

通常我们声明类的成员函数时候都是用匿名函数来声明的,因为反正那个函数也就是这个类的一个成员函数而已,不会在其它地方被单独引用或者调用,所以就有了下面的代码:

function fun(param) {if(param === undefined) {this.print = "hello wolrd";} else {this.print = param;}
}
fun.prototype.Print = function(param) {this.print = param;
}var fun1 = new fun("hello");
fun1.Print("不好意思,我这里写个BUG");
console.log(fun1.print); 

class传统方式定义类

class Rect {//构造函数constructor(w, h) {this.width = w;this.height = h;}//类成员函数toString() {return '(' + this.width + ', ' + this.height + ')';}//类静态成员函数static setParam(name){//修改类静态成员变量this.para = name;return   this.para;}
}
//初始化类静态成员变量
Rect.para = 'hello world';//新创建Rect对象
var rect = new Rect(1024, 960);
console.log(rect.toString());
console.log(Rect.para);
console.log(Rect.setParam('高司机'));
console.log(Rect.para);

好了,这代码块相信你应该看的懂(如果不懂可以继续给我留言,我将完善尽可能让更多的人没有阅读理解障碍。),类静态成员函数也是通过static来修饰,静态成员也是在类外部进行声明并初始化。构造函数一般是通过constructor来声明。

光会写这块代码,我相信你肯定不满足,那么我们继续根据这个代码做变化,更加熟悉nodejs的类:

1.能不能有多个构造函数呢?

相信你已经跃跃欲试了

class Rect {constructor(w, h) {this.width = w;this.height = h;}constructor(w, h, z) {this.width = w;this.height = h;}Rect(){console.log("call Rect\n");}toString() {return '(' + this.width + ', ' + this.height + ')';}static setParam(name){this.para = name;return this.para;}
}
Rect.para = 'hello world';var rect = new Rect(1024, 960);
console.log(rect.toString());
console.log(Rect.para);
console.log(Rect.setParam('高司机'));
console.log(Rect.para);

然鹅,实际运行中报错了:

/root/app1.js:7constructor(w, h, y) {^^^^^^^^^^^SyntaxError: A class may only have one constructorat createScript (vm.js:56:10)at Object.runInThisContext (vm.js:97:10)at Module._compile (module.js:549:28)at Object.Module._extensions..js (module.js:586:10)at Module.load (module.js:494:32)at tryModuleLoad (module.js:453:12)at Function.Module._load (module.js:445:3)at Module.runMain (module.js:611:10)at run (bootstrap_node.js:394:7)at startup (bootstrap_node.js:160:9)

所以呀nodejs的类只有一个构造函数

 2.能不能像C++一样通过类名这样的方式声明一个构造函数

比如:

class Rect {Rect(){console.log("call Rect");}toString() {return '(' + this.width + ', ' + this.height + ')';}static setParam(name){this.para = name;return this.para;}
}
Rect.para = 'hello world';var rect = new Rect();
rect.Rect();

你会发现实际上 var rect = new Rect()并不会调用Rect函数,而只有rect.Rect()才会调用Rect方法。 

3.如果创建对象的时候传入构造函数的参数少呢?


class Rect {constructor(w, h) {this.width = w;this.height = h;console.log("call  constructor\n");}Rect(){console.log("call Rect\n");}toString() {return '(' + this.width + ', ' + this.height + ')';}static setParam(name){this.para = name;return this.para;}
}
Rect.para = 'hello world';var rect = new Rect(1024);
console.log(rect.toString());

输出的结果:

call  constructor(1024, undefined)

类的自由性

所谓类的自由性即你可以在任何地方修改你的类,比如说 String类,有着诸如 length 这样的成员变量,也有 indexOfsubstr 等成员函数。但是万一我们觉得这个 string 有些地方不完善,想加自己的方法,那么可以在你想要的地方给它增加一个函数,比如:

String.prototype.fun = function() {return "hello world";
};

这个string类的fun函数的意思就是返回hello world。

我们来测试一下:

var str = "高司机又要开始写bug了";
console.log(str.fun());

你将会得到这样的结果:

hello world

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode38. 外观数列

「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。前五项如下&#xff1a; 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作 "one 1" ("一个一") , 即 11。 11 被读作 "two …

(五)nodejs循序渐进-回调函数和异常处理(基础篇)

上篇文章我们讲完了类和对象&#xff0c;接下来我们将要说回调函数. 我在第一篇说到nodejs的一个优势是异步IO&#xff0c;实际上异步IO直接体现就是使用回调函数&#xff0c;当然不是用了回调函数&#xff0c;他就一定是异步IO的&#xff0c;因为inodejs是一个单线程函数&…

(六)nodejs循序渐进-数据流和文件操作(基础篇)

Buffer JS 语言自身只有字符串数据类型&#xff0c;没有二进制数据类型&#xff0c;因此 NodeJS 提供了一个与 String 对等的全局构造函数 Buffer 来提供对二进制数据的操作。除了可以读取文件得到 Buffer 的实例外&#xff0c;还能够直接构造&#xff0c;Buffer 与字符串类似…

leetcode171. Excel表列序号

给定一个Excel表格中的列名称&#xff0c;返回其相应的列序号。 例如&#xff0c; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 示例 1: 输入: "A" 输出: 1 示例 2: 输入: "AB" 输出: 28 …

(七)nodejs循序渐进-模块系统(进阶篇)

模块系统 为了让Node.js的文件可以相互调用&#xff0c;Node.js提供了一个简单的模块系统。 模块是Node.js 应用程序的基本组成部分&#xff0c;文件和模块是一一对应的。换言之&#xff0c;一个 Node.js 文件就是一个模块&#xff0c;这个文件可能是JavaScript 代码、JSON 或…

(八)nodejs循序渐进-事件驱动(进阶篇)

事件驱动程序 Node.js 使用事件驱动模型&#xff0c;当web server接收到请求&#xff0c;就把它关闭然后进行处理&#xff0c;然后去服务下一个web请求。 当这个请求完成&#xff0c;它被放回处理队列&#xff0c;当到达队列开头&#xff0c;这个结果被返回给用户。 这个模型…

leetcode304. 二维区域和检索 - 矩阵不可变

给定一个二维矩阵&#xff0c;计算其子矩形范围内元素的总和&#xff0c;该子矩阵的左上角为 (row1, col1) &#xff0c;右下角为 (row2, col2)。 上图子矩阵左上角 (row1, col1) (2, 1) &#xff0c;右下角(row2, col2) (4, 3)&#xff0c;该子矩形内元素的总和为 8。 示例…

(九)nodejs循序渐进-Express框架(进阶篇)

Express 框架 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用&#xff0c;和丰富的 HTTP 工具。 使用 Express 可以快速地搭建一个完整功能的网站。 Express 框架核心特性&#xff1a; 可以设置中间件来响应 HTTP 请求。 定…

leetcode326. 3的幂 如此6的操作你想到了吗

给定一个整数&#xff0c;写一个函数来判断它是否是 3 的幂次方。 示例 1: 输入: 27 输出: true 示例 2: 输入: 0 输出: false 示例 3: 输入: 9 输出: true 示例 4: 输入: 45 输出: false 进阶&#xff1a; 你能不使用循环或者递归来完成本题吗&#xff1f; 注意最后一句…

(十)nodejs循序渐进-高性能游戏服务器框架pomelo之介绍和安装篇

目录 Pomelo 安装Pomelo 创建demoserver项目 pomelo命令 项目结构说明 pomelo框架 架构 服务器实现 客户端请求与响应、广播的抽象介绍 Pomelo pomelo是一个快速、可扩展、Node.js分布式游戏服务器框架&#xff0c;对游戏服务器开发感兴趣的同学可以关注关注。 之前…

leetcode344. 反转字符串 史上最简单力扣题

编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。…

(十一)nodejs循序渐进-高性能游戏服务器框架pomelo之启动流程和组件

游戏启动过程 启动入口 在使用pomelo进行游戏开发时&#xff0c;工程目录下的app.js是整个游戏服务器的启动运行入口。app.js中创建项目&#xff0c;进行默认配置并启动服务器的代码如下&#xff1a; var pomelo require(pomelo); var app pomelo.createApp(); app.set(na…

(十二)nodejs循序渐进-高性能游戏服务器框架pomelo之创建一个游戏聊天服务器

上个章节我们简单介绍了下pomelo的安装和目录结构&#xff0c;有读者可能觉得有点吃不消&#xff0c;为什么不再深入讲一讲目录结构和里边的库&#xff0c;这里我就不费口舌了&#xff0c;大家可以去官网参考文档说明&#xff0c;本文只告诉大家如何利用这个框架来开发自己的东…

看这玩意复习你还会挂科?《软件工程篇》

软件工程&#xff1a;是指导软件开发和维护的一门工程学科 三要素方法/工具/开发过程 价值&#xff1a;促进项目成功 现代产品开发三原则&#xff1a;功用性、可行性、称许性 软件过程是软件工程的核心组成部分。 迭代 &#xff1a;反复求精 增量&#xff1a;逐块建造 需…

C++:02---命名空间

一、概念: ①类似于仓库,空间内存储代码,需要用到时调用②也为防止名字冲突提供了更加可控的机制二、命名空间的定义 定义的基本格式如下:namespace 命名空间名 { //一系列声明与定义 };三、命名空间的注意事项 命名空间定义时最后的分号可有可无只要出现在全局作用域中的…

看这玩意复习你还会挂科?《软件工程2篇》

第一章&#xff1a; 软件工程定义&#xff1a; 1968年10月&#xff0c;Fritz Bauer 首次提出了“软件工程”的概念&#xff0c;并将“软件工程”定义为&#xff1a;为了经济地获得能够在实际机器上有效运行的可靠软件&#xff0c;而建立并使用的一系列工程化原则。 1993年IE…

C++:05---命名空间

一、概念: ①类似于仓库,空间内存储代码,需要用到时调用②也为防止名字冲突提供了更加可控的机制二、命名空间的定义 定义的基本格式如下:namespace 命名空间名 { //一系列声明与定义 };三、命名空间的注意事项 命名空间定义时最后的分号可有可无只要出现在全局作用域中的…

C++:04---内联函数

1.概念: 内联类似于宏定义,当程序执行到内联函数时,相当于复制了一份函数代码。牺牲代码空间,赢得了时间 内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求 2.关键字:inline 声明时写了inline,定义时可省略。建议声明和定义都加上inlineinline int add(int…

leetcode86. 分隔链表

给定一个链表和一个特定值 x&#xff0c;对链表进行分隔&#xff0c;使得所有小于 x 的节点都在大于或等于 x 的节点之前。 你应当保留两个分区中每个节点的初始相对位置。 示例: 输入: head 1->4->3->2->5->2, x 3 输出: 1->2->2->4->3->5…

(十三)nodejs循序渐进-高性能游戏服务器框架pomelo之扩展聊天服务器为机器人自动聊天

聊天服务器扩展 大家在上一篇文章里相信已经学会了pomelo框架的基本用法了&#xff0c;那么我们在上一篇文章的代码基础上继续扩展&#xff0c;丰富系统&#xff0c;另外也熟悉下他的更多的用法&#xff0c;这一节我将扩展它&#xff1a;增加一个机器人自动聊天的功能。 目的…