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=functionparams){}

上面方法在内存上是怎么实现的呢?其实也很简单,看下面内存分配图就会明白了:
其实,上述的方法中,无论是第一种方法还是第二种方法,在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应该注意,而是学习任何一门语言都应该注意和掌握的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值