Node.js基础知识杂烩

Node.js基础知识杂烩

这段时间做项目主要做关于服务器的功能点,因此,逐渐开始学习node.js写服务器,总体下来,觉得node比php更好用,写服务前器的速度更快,处理效率更高。注:node并不是专门写服务器的,其实node的出现,将js从web前端的专用语言,变成了一门通用语言。node中有各样的基础模块:比如fs,path,buffer,http,net等模块,此外,node还有各种各样的三方模块,比如写服务器人常用的express,co,koa,seqlize等著名框架,也就是三方模块。那么,node.js的模块机制究竟是如何的呢?我们应该注意模块机制上的哪些问题呢?

1.关于Nodejs的模块机制的一些疑问

  1. 什么是node.js的模块?

    Node.js的模块机制遵从CommonJS的规范,CommonJS定义模块的概念如下:

    • 一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。
    • 每个文件的对外接口是module.exports对象。这个对象的所有属性和方法,都可以被其他文件导入。我们可以通过module.exports输出这个模块定义变量,函数,类,
    • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  2. require加载同一个模块多次,加载的模块都是一样的吗?

    下面给出一段代码,我们根据代码来看示例:

    module1模块:

var num=1;
module.exports.num=num;`
   a 文件:
   `var module1=require("./module1");
   var module2=require("./module1");
   module1.num=2;
   console.log(module2.num);

上述代码运行结果是多少呢?很多人应该都会回答2,答案确实也是2,但是如果下面这种场景呢?

`module1模块:

var num=1;
module.exports.num=num

a 文件:

`var module1=require("./module1");
module1.num=2;`

b 文件:

var module2=require("./module1");
console.log(module2.num)

现在,先运行a文件(注意,a文件运行完成后,node并未结束),然后在运行b文件,请问num是多少?这里很多人都会说,显然是1啊。这是一种惯性思维,其实答案是2,为什么呢?因为模块无论在同一个文件还是在不同的文件中被加载,如果被加载过,都会保留他们的缓存,也就是只是被加载一次。所以,这里在a文件运行后,module1模块的num数值被加载修改为2,在b文件运行的时候,module1模块因为被加载过,因此,num的数值在b文件中,显示为2。

  1. 模块中的变量和函数都是静态的吗?

    是的。因为CommonJs的规范,模块都是被加载一次,那么,每次模块被加载的实例都是一样的,这和static变量或者是函数,都是一样的。因此,对于node而言,模块之间的变量和函数都是静态的。

  2. 对比下node的required机制和java的import机制?

    对node而言,引入模块是使用require命令,而java是import命令。node的require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。而java的import是引入一个类文件,然后通过类文件名,来访问文件中的方法或者变量。

    由此可以看出,node的require和java的import都有相似的设计思路:

    • 两者都是通过文件(模块)的方式来进行作用域的隔离
    • 两者都进行的访问的控制:java有priavte,public访问符号,而node有exports是否输出决定该文件中的变量。

    两者不同的之处在于:

    • 1.node的require命令引入是一个module对象,而java的import命令引入的是一个类。
    • 2.node的required命令引入一个module对象之后,我们可以直接访问module对象中的一些属性,函数,但是java可能需要通过一个类来实例化一个对象,然后访问这个对象的某些属性,当然,java中的类上的static变量,方法就可以不实例化而进行访问。

2.关于js的函数的一些疑问

函数是js中一个最关键的东东,在java语言中,函数仅仅是传统意义上的函数,执行一个函数,访问对象的代码区,然后为参数分配对象的空间,接下来执行函数就可以了,然后返回值。但是对js而言,函数不单单是函数,函数也可以作为变量,参数,返回值,函数甚至再js中作为一种基本类型,对!函数在js中是一种对象类型,也是就是说,函数在js中一个对象而存在。其实将函数作为一种基本类型,也就说明了函数的灵活性。那么,我们有必要分析下js的函数是如何实现的

js有两种常用的定义函数的方法:

function A(){}`
  `var a=function(params){}

上面方法在内存上是怎么实现的呢?其实也很简单,看下面内存分配图就会明白了:

其实,上述的方法中,无论是第一种方法还是第二种方法,在js的解释器中,都会这样执行:

    var x=new Object
     x.constructor=A;    `
    `A.prototype=x

这里的x就是上图右边的object。可以看到,js的函数的本质上是指向了一块堆空间(因为已经有原型了:A.prototype=obejct)。这也很好地解释了函数为什么能被作为参数和返回值,因为一个函数名作为参数的时候,代表的就是一块堆空间上的一块地址值了。上述两种方法里面,不同的是第一种方法,name=A,而第二种方法的name=undefined。

所以,现在可以明确一个写node或者js的程序时候的思想了:当我定义一个函数的时候,那么,随即会在堆空间上定义一个对象,然后用函数名代表这个对象的地制值,调用这个函数的时候,即访问这个对象,届时,再分配栈空间,执行代码段,进而执行函数。

3.js的类型,参数,作用域,闭包

  1. js是不做类型检查的一门语言,那么js的类型有哪些呢?《javascript高级程序设计》大致基本类型分为基础类型和对象类型,基础类型包括:布尔类型,整形,string,undefined和null。而对象类型包括Object,Funtion,Array,Date。(这里再次可以看出,funtion是对象类型)
  2. js中定义函数的参数时,并不写类型声明(这也是js的一个弊端所在),因此,在调用js的函数的时候,程序员务必自己注意类型。关于参数容易出现的问题是将json的string类型和object类型搞混淆,function类型使用错误等,都是项目中常见的问题。
  3. 作用域:关于js的作用域的问题,详细了解可以看这篇博客:js作用域详解,关于js传统的作用域的问题,这里简单表述下:
    • 1.JavaScript的是有作用域链存在的。
    • 2.JavaScript没有块级作用域。
    • 3.JavaScript具有变量提升的特点。
    • 4.函数体内部,局部变量的优先级比同名的全局变量高(局部变量会掩盖全局变量的带来的作用域)

    我们可以看到,由于js的规定的作用域问题,我们可能会带来一些困扰,比如下面的这个函数:

var foo = 1;
   function bar() {
    if (!foo) {
        var foo = 10;
    }
    console.log(foo);
 }
bar();

执行的结果是多少呢?很多人会说是1,但是,由于js的函数内部定义的变量优先级比外部的高,加之js的变量提升的原因,因此,上述执行的结果执行为10;

再看这个函数:

    function test () {
    console.log(value);
    var value = ‘something‘;
    console.log(value);
}

这样写法在其他有块级作用域中显然是不会编译通过的,但是在js中可以的,也是因为了js的变量提升,可以编译通过,输出结果为undefined和something。

以上的问题,就是js的作用域设计问题带来的不好的效果,比如没有块级作用域以及变量提升等问题,会在编程上带来一些不理想的效果。这些问题,在ES6的规范中,用let代替var后,得到解决。比如上述问题中第一个,可以使用let来代替var:比如这样解决:

var foo = 1;
function bar() {
    if (!foo) {
        let foo = 10;
    }
    console.log(foo);
}
bar();

4.js的有几种方法如何实例化一个对象?

对于js而言,要实例化一个对象有以下几种方法:

1. 书面语句:

var person = {
    name: "lbq",
    age: 22,
    getName: function () {
        return this.name;
    }
}
console.log(person.getName());

可以看到,这样创建一个object确实很简单,只要一个花括符就可以,这也是js创建对象最方便的一点。

2. 使用函数创建:

 function Person(name,age){
    this.name=name;
    this.age=age;
    this.getName=function(){
        return this.name;
    }
}
var person1=new Person("lbq",23);
var person2=new Person("lzw",22);
console.log(person1.getName());
console.log(person2.getName());

大家可以看到,第二中方法是通过function来构造生成的object,这里可以看到,js在ES6之前,没有类的概念,我们要构造对象,只有通过function来生成。

  1. 原型方式生成对象:
function Person2(name, age) {
    this.name = name;
    this.age = age;
}
Person2.prototype.getName = function () {
    return this.name;
}
Person2.prototype.getAge = function () {
    return this.age;
}
var person3=new Person2("lbq",23);
var person4=new Person2("lzw",22);
console.log(person3.getAge());
console.log(person4.getAge());

在js中,每个函数都是有一个prototype属性,这个属性就是一个指针,指向一个对象,这个对象就是原型对象。我们可以对原型对象进行变量定义和方法定义,然后通过new来实例化一个对象,实例化的对象会有一个——proto—,指向原型对象。关于原型对象的具体可以参考《Javascript高级程序设计》这本书的第六章,有详细介绍。

  1. ES6新语法,通过class关键字来实例化一个对象:
 class Person3{
    constructor(name,age){
        this.name=name;
        this.age=age;
    }

    getName(){
        return this.name;
    }

    getAge(){
        return this.age;
    }
}
var person5=new Person3("lbq",23);
var person6=new Person3("lzw",22);
console.log(person5.getName());
console.log(person6.getName());

好的消息是,在ES6中,加入class关键字后,我们可以在js中,像java那样定义类,然后通过类来实例化一个对象了。

5.js的this关键字的理解

this关键在java中也许很好理解,因为js是面向对象的语言,因此,java中的this就是指向当前的对象,我们可以通过this来访问当前对象的成员变量或者方法(无论公有还是私有),但是,js是一门函数式编程的语言,js中的this又怎么理解呢?

关于js的this的理解,可能说的比较复杂,这里我给出我的自己的理解:出现有this关键字的函数,是哪个对象中定义的函数,那么,该对象就是thi指向的对象:

我们来看下示范:

var obj={};

obj.x=100;

obj.y=function (){console.log(this.y)};

obj.y();

这里就很好理解了:显然打印出来的就是100,因为函数y是在obj变量中被调用的,所以thi指向的是obj对象,this.x就是100.

现在我们看一个难一点的示范:

var showthis=function(){

console.log(this.x);

}

var x=”this is a variable of window”;

var obj={};

obj.x=100;

obj.y=function (){console.log(this.x)};

var y2=obj.y;

obj.y();

showthis():

y2():

上述代码中,一共要打印三个console,按照分析,一个肯定还是100,第二个呢?在JavaScript的变量作用域里有一条规则“全局变量都是window对象的属性“。所以,showthis函数变量是在window对象中定义的,因此,showthis打印的是this is a variable of window。那么,y2对象呢?y2对象也是在window对象中定义的,因此,第三个console的结果,也一定是this is a variable of window。

经过上述两个案例,我们可以看到,this就是指向函数所在的对象的,如果函数在全局,那么,this就是指向window对象了。

关于this指向的思考,我是从node构建web项目来理解的,因为在koa中,经常用this.request或者是this.body来指代http的报文请求和应答,所以,有些时候务必要注意程序的中的this指向。如果想改变this的环境,那么,可以使用call或者是apply。

以上就是关于我在初学node.js中的一些关键疑问,比如js的函数如何作为一等公民对待,内存分配长啥样,还有关于初学一门语言都应该掌握的基础:变量类型,作用域,常会犯错的关键字,依赖体系的建立等,不是学习node应该注意,而是学习任何一门语言都应该注意和掌握的

时间: 2024-10-01 13:52:51

Node.js基础知识杂烩的相关文章

NodeJs>------->>第三章:Node.js基础知识

第三章:Node.js基础知识 一:Node.js中的控制台 1:console.log.console.info  方法 1 console.log("this is a test string."); 1 node app1.js 1> info.log 1 console.log("This is a test String ."); 2 3 //从第二个参数开始,依序输出所有的字符串 4 console.log("%s","

Node.js基础知识普及

Node.js只支持单线程,故不会产生死锁,采用非阻塞I/O机制和事件环机制.非常适合与开发需要处理大量并发的输入/输出的应用程序.  一. Node.js的核心模块有很多,这里先写几个比较常用的(目前我个人较常用的,不代表大众):   1.buffer:用于实现二进制数据的存储和转换:   2.child-process:用于实现子进程的创建与管理:   3.console:用于控制台输出信息:   4.events:用于为事件处理提供一个基础类:   5.fs:用于操作文件及文件系统:  

Node.js权威指南 (3) - Node.js基础知识

3.1 Node.js中的控制台 / 19 3.1.1 console.log方法 / 19 3.1.2 console.error方法 / 20 3.1.3 console.dir方法 / 21 3.1.4 console.time方法与console.timeEnd方法 / 22 3.1.5 console.trace方法 / 22 3.1.6 console.assert方法 / 233.2 Node.js中的全局作用域及全局函数 / 23 3.2.1 Node.js中的全局作用域 / 2

Node.js基础知识

## 什么是Node.js?1. Node.js是一门后端语言2. 基于Chrome v8引擎的js运行环境除此之外:还有其他的引擎: 单片机.actionscript3. Node.js有两大特征1. 事件驱动1. 事件的定义(发布)2. 事件的触发(订阅)2. 非阻塞式 I/O 模型1. 非阻塞 : 执行一个任务时,不需要等待这个任务执行完成,就可以去执行其他任务2. I/O: 磁盘文件操作 / 数据库数据操作总结:问题: 非阻塞,但是js是单线程,要想实现非阻塞,必须将任务放到任务队列,任

JS基础知识回顾:引用类型(一)

在ECMAScript中引用类型是一种数据结构,用于将数据和功能组织在一起,而对象时引用类型的一个实例. 尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构,所以虽然说引用类型与类看起来想死,但他们并不是相同的概念. 不过引用类型有的时候也可以被称为对象定义,因为他们描述的是一类对象所具有的属性和方法. 新对象是使用new操作符后跟一个构造函数来实现的,构造函数本身就是一个函数,只不过该函数时处于创建新对象的目的而定义的. ECMASc

node.js基础模块http、网页分析工具cherrio实现爬虫

node.js基础模块http.网页分析工具cherrio实现爬虫 一.前言      说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路径对应网页资源,然后使用cherrio分析. 这里我主要学习过的案例自己敲了一遍,加深理解.在coding的过程中,我第一次把jq获取后的对象直接用forEach遍历,直接报错,是因为jq没有对应的这个方法,只有js数组可以调用. 二.知识点    ①:supera

JS 基础知识4 运算符

JS的运算符一般含有: 1 算术运算符 2 相等运算符 3 关系运算符 4 字符串运算符 5逻辑运算符 6位运算符 7 赋值运算符 首先看算术运算符,它一般有(+,-,*,/,%,++,--) 这里,%模运算符,其实也就是取余的意思,比如,5%2=1: 这里要特别说一下++,与--(这里只列了++,--是一样的道理). 1 var i = 1; 2 var j = ++i; //实际相当于 i+=1;j=i; 3 alert(i + " " + j); 4 5 var m = 1; 6

JS 基础知识2 传值和传址

要知道传址跟传址,首先我们先了解下数据值的操作. 1复制:可以把它赋值给一个新的变量 2传递:可以将他传递给一个函数或者方法 3比较:可以与另外一个值比较,判断是否相等 简要介绍下传值: 当一个数据是通过值被操作的,那么关系到的是数据的值,在赋值的过程中,是对实际值进行了COPY,储存在一个变量或者属性或数组中,copy的值与原数据是相互独立的. 当数据通过值传递给一个函数时,数据的一份传递给这个函数,如果函数体修改了这个值,只在函数体受影响,函数外的原数据不受影响. 当一个数据通过值和另外一个

JS 基础知识3 变量

变量和数值相关,它储存了那个值,有了变量就可以储存操作数据了. js与其他语言不同,它是非类型的.就是变量可以存放任何类型的值,而其他语言需要存放特定类型的值. var i=5; i="fdsfad"; 这是合法的. 变量的声明一般是由VAR 关键字声明的 var i,sum; //一次声明两个变量,   若变量没有给定初始值,则值为“undefined” 在JS中多次声明同一个变量,是不会出错的,仅仅是给变量赋值的性质. 还有一种,不用var关键字声明变量,则JS会隐式的声明该变量,