Node学习总结-基础篇

1、谈谈js中的变量类型有哪些?

答:值类型:字符串(string)、数值(number)、布尔值(boolean)、undefined、null、symbol(es6)

引用类型:对象(Object)、数组(Array)、函数(Function)

2、谈谈null和undefined的区别?

答:null是一个表示 "无" 的对象,转为数值时为 0;undefined是一个表示 "无" 的原始值,转为数值时为NaN。

3、谈谈值类型和引用类型的区别?

答:值类型:

  • 1、占用空间固定,保存在栈内存中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。)
  • 2、保存与复制的是值本身。
  • 3、使用typeof检测数据的类型。

引用类型:

  • 1、占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)
  • 2、保存与复制的是指向对象的一个指针
  • 3、使用instanceof检测数据类型
  • 4、使用new()方法构造出的对象是引用型

4、谈谈栈内存和堆内存的区别?

答:首先要明白数据结构中的栈和堆:

  • 栈 :这是一种连续存储的数据结构,具有先进后出的性质。
  • 堆 :是一种非连续的树形存储数据结构,每个节点都有一个值,整棵树是经过排序的。

其次,内存中的栈区和堆区:

  • 栈内存 :由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。
  • 堆内存 :程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则可能导致内存泄露。

5、谈谈对js作用域的认识。(可能会引出变量提升)

答:在早期的ES5中,js的作用域分为两种,分别为全局作用域和局部作用域。之后在ES6中添加了块级作用域,主要通过命令let和const来实现。

ES5只有全局作用域和函数作用域,没有块级作用域,会带来以下问题:

  • 1.变量提升导致内层变量可能会覆盖外层变量。
  • 2.用来计数的循环变量泄露为全局变量等等。

5、简要说明var、let、const的区别。

答:1.const定义的变量不可以修改(引用类型可以修改,但是引用始终不变,变得是在堆中的对象或者数组或者函数),而且必须初始化。

2.var定义的变量可以修改,如果不初始化会输出undefined,不会报错。

3.let是块级作用域,函数内部使用let定义后,对函数外部无影响。

6、js中==和===的区别。

答:简单来说: == 代表相同, === 代表严格相同(类型也必须相同)。

当进行双等号比较时候:先检查两个操作数数据类型,如果相同,则进行===比较,如果不同,则愿意为你进行一次类型转换,转换成相同类型后再进行比较,而===比较时,如果类型不同,直接就是false。

7、遍历一个json的方法。

答:1、for … in

2、Object.keys(obj).forEach(key)

3、Object.getOwnPropertyNames(obj).forEach(key)

4、Reflect.ownKeys(obj).forEach(key)

8、合并数组的方法。

答:1、for循环 + push()

2、concat()

3、ES6的扩展运算符…

当数组中有引用对象时,第2和3种是浅拷贝。

9、谈谈浅拷贝和深拷贝的区别。

答:浅拷贝:只是拷贝索引,不拷贝对象。原对象变化时,新对象也会随之变化。

深拷贝:拷贝对象。数组的JSON.parse(JSON.stringify(arr))

6、谈谈node中的require加载原理。

答:在node中,每一个js文件相当于一个module(这里node和前端是有区别的),它都会有其独自的变量空间,require相当于在自己的模块中,拿到其他模块的引用。

同一个模块被多个模块引用时,该模块的内容只执行一次,第二次加载时会直接在缓存中加载(require.cache)。

两个模块互相循环引用,并不会造成死循环。(一个模块获取另一个模块的全部引用,而一个模块只会获取另一个模块的未完成的副本)。

大概加载流程:

  • 计算绝对路径
  • 如果有缓存,取出缓存
  • 是否为内置模块
  • 生成模块实例,存入缓存
  • 加载模块
  • 输出模块的exports属性

8、谈谈module.exports和exports的区别。

答:exports只是module.exports的引用,如果module.exports被直接赋值,则exports的赋值不会起作用,例如module.exports='hanjc'。

但是如果是module.exports.name这种增加属性,则exports会起作用。

8、谈谈js中的this指向。

答:1、普通函数调用:这个情况没特殊意外,就是指向全局对象-window。

2、对象函数调用:这个相信不难理解,就是哪个函数调用,this指向哪里。

9、谈谈apply, bind, call这些方法,他们之间的区别。

答:call、apply、bind 都是用来重定义 this 这个对象的。

call、apply、bind 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔。apply 的所有参数都必须放在一个数组里面传进去。bind 除了返回是函数以外,它的参数和 call 一样。

13、Promise的几种状态。

答:promise有三种状态:pending状态(进行中)reslove状态(已成功)reject状态(已失败)

14、Promise.all的功能及实现。

答:Promise.all接受一个Promise数组,并行执行数组中的所有异步操作,返回结果集。数组里面的promise是并行执行的。

15、async/await相比于promise的优点。

答:1. 简洁(if判断,嵌套等等地方)。2.错误处理(直接用try/catch)。

16、async/await大致的实现原理。

答:async函数是Generator函数的语法糖,用function*定义,方法内用yeild标识,用return返回({value,done}),执行时用next进行下一个步骤。

co 模块:用于 Generator 函数的自动执行,返回一个Promise对象。

17、谈谈express中的中间件。

答:express是一个自身功能极简,完全是路由和中间件构成一个web开发框架:从本质上来说,一个Express应用就是在调用各种中间件。

  • 1、中间件结构app.use([path],function)
  • 2、中间件分类:内置中间件(express.static唯一的内置中间件)、自定义中间件(自己实现的)、第三方中间件(npm导入的中间件,例如bodypaser等等)
  • 3、浏览器向服务器发送一个请求后,服务器直接通过request.定位属性的方式得到通过request携带过去的数据(有用户输入的数据和浏览器本身的数据信息)。这中间就一定有一个函数将这些数据分类做了处理,已经处理好了,最后让request对象调用使用,对的,这个处理数据处理函数就是我们要说的中间件。

由此可见,中间件可以总结以下几点:

  • 1、封装了一些处理一个完整事件的功能函数。
  • 2、非内置的中间件需要通过安装后,require到文件就可以运行。
  • 3、封装了一些或许复杂但肯定是通用的功能。

18、谈谈node单线程、异步IO与事件驱动的理解。

答:nodejs的运行机制:nodejs主线程主要起一个任务调度的作用。nodejs用一个主线程处理所有的请求,将I/O操作交由底下的线程池处理。在所有主线程任务执行完成后,主线程处理事件队列。所以在同步初始化代码执行完成后,nodejs会基于事件队列不停的做事件循环。事实上,nodejs运行环境 = 主线程(单线程,包括事件队列) + 线程池(工作线程池,执行其他工作-多线程)

node 的初始化:

  • 初始化 node 环境。
  • 执行输入代码。
  • 执行 process.nextTick 回调。
  • 执行 microtasks。(Promise.then)

事件循环

  • 1、进入 timers 阶段 (定时器阶段:本阶段执行已经安排的 setTimeout() 和 setInterval() 的回调函数。)
  •       检查 timer 队列是否有到期的 timer 回调,如果有,将到期的 timer 回调按照 timerId 升序执行。
  •       检查是否有 process.nextTick 任务,如果有,全部执行。
  •       检查是否有microtask,如果有,全部执行。
  •       退出该阶段。
  • 2、进入pending IO callbacks阶段。(对某些系统操作(如 TCP 错误类型)执行回调)
  •       检查是否pending的 I/O 回调。如果有,执行回调。如果没有,退出该阶段。
  •       检查是否有 process.nextTick 任务,如果有,全部执行。
  •       检查是否有microtask,如果有,全部执行。
  •       退出该阶段。
  • 3、进入 idle,prepare 阶段:
  •       仅系统内部使用。
  • 4、进入 poll 阶段(检索新的 I/O 事件;执行与 I/O 相关的回调,除了定时器和关闭的回调函数,其余都在这里)
  •       首先检查是否存在尚未完成的回调,如果存在,那么分两种情况。
  •       第一种情况:
  •               如果有可用回调(可用回调包含到期的定时器还有一些IO事件等),执行所有可用回调。
  •               检查是否有 process.nextTick 回调,如果有,全部执行。
  •               检查是否有 microtaks,如果有,全部执行。
  •               退出该阶段。
  •        第二种情况:
  •                如果没有可用回调,执行下一步;
  •                检查是否有 immediate 回调,如果有,退出 poll 阶段。如果没有,阻塞在此阶段,等待新的事件通知。
  •                如果不存在尚未完成的回调,退出poll阶段。
  • 5、进入 check 阶段。(setImmediate() 回调函数在这里执行)
  •       如果有immediate回调,则执行所有immediate回调。
  •       检查是否有 process.nextTick 回调,如果有,全部执行。
  •       检查是否有 microtaks,如果有,全部执行。
  •       退出 check 阶段
  • 6、进入 closing 阶段。(检测关闭的回调函数,例如 xx.on('close'))
  •       如果有immediate回调,则执行所有immediate回调。
  •       检查是否有 process.nextTick 回调,如果有,全部执行。
  •       检查是否有 microtaks,如果有,全部执行。
  •       退出 closing 阶段
  •       检查是否有活跃的 handles(定时器、IO等事件句柄)。
  •       如果有,继续下一轮循环。
  •       如果没有,结束事件循环,退出程序。
  •       注: 在主线程执行完和事件循环总共7个阶段,每一个阶段执行完都会调用一遍process.nextTick回调,一遍microtaks(promise);

5、谈谈nodejs中进程间通讯的方式?

答:1、通过stdin / stdout传递json。

用spawn方法拿到子进程的handle,然后用通过stdin/stdout传递json。

2、原生IPC支持。

同样要有一方能够拿到另一方的handle才行。

3、sockets。

4、message queue。

19、node内存管理情况。

答:在node.js中,内存主要分为两个部分,堆内存和栈内存。

  • 堆内存(heap):存放对象和闭包上下文,v8使用垃圾回收机制管理堆内存。
  • 栈内存(stack):存放局部变量,栈内存的分配比较简单,当程序离开某作用域后,其栈指针下移(回退),整个作用域的局部变量都会出栈被回收。

24、新建Buffer会占用V8分配的内存吗。

答:不会,Buffer属于堆外内存,不是V8分配的。

25、Buffer.alloc和Buffer.allocUnsafe的区别。

答:Buffer.allocUnsafe创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,可能包含敏感数据。使用 Buffer.alloc() 可以创建以零初始化的 Buffer 实例。

26、Buffer的内存分配机制。

答:为了高效的使用申请来的内存,Node采用了slab分配机制。slab是一种动态的内存管理机制。Node以8kb为界限来来区分Buffer为大对象还是小对象,如果是小于8kb就是小Buffer,大于8kb就是大Buffer。

例如第一次分配一个1024字节的Buffer,Buffer.alloc(1024),那么这次分配就会用到一个slab,接着如果继续Buffer.alloc(1024),那么上一次用的slab的空间还没有用完,因为总共是8kb,1024+1024 = 2048个字节,没有8kb,所以就继续用这个slab给Buffer分配空间。

如果超过8kb,那么直接用C++底层地宫的SlowBuffer来给Buffer对象提供空间。

27、Buffer乱码问题。

答:rs.setEncoding('utf8')

      (我试过iconv解决,只不过电脑得提前安装一下)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值