js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递?
一般情况下,简单数据类型是在栈里面直接储存一个数值,赋值操作直接改这个值,就是值传递的
如果是复杂数据类型,及Object和Object延伸出的其他复杂类型,都是先在栈里储存一个指针,然后指针指向堆空间里的数值,而进行赋值传递的时候,只是另一个变量也仅储存了这个指针,就导致改这个对象,另一个对象也跟着变的,就是引用传递
js 中, 0.1 + 0.2 === 0.3 是否为 true ? 在不知道浮点数位数时应该怎样判断两个浮点数之和与第三数是否相等?
不相等,因为JS浮点数先转2进制再计算再转十进制的原因,会丢失精度,所以false了
尽量避免浮点数比较吧,如果非要比的话,我这边一般两种做法吧
一种是标准做法:
JS里,最大整数和最接近零的小数,分别是2的正负52次方
而最接近0的小数,也可以用Number.EPSILON
表示
如果Math.abs((0.1+0.2)-0.3)
比如算0.1+0.2,我就会(0.1+0.2).toFixed(15)*1
因为那个最接近0的小数,它其实是0.00..02xx,中间15个零
缺点嘛一方面toFixed肯定性能比人家自带的常量会差一点,而且如果真的有两个15位小数计算,toFixed这个有误差,而EPSILON无误差。优点就方便
实际情况的话,如果我抽成公共方法了,那我就用常量,如果临时写业务,可能就toFixed了,因为读代码比较方便易懂
const 定义的 Array 中间元素能否被修改? 如果可以, 那 const 修饰对象的意义是?
能修改,const相当于把栈里的数据锁死了,Array是个引用数据类型,只是锁死了地址,堆里面的数据依然可以随便改
内存释放
node自带垃圾回收机制了,node的GC机制,就是从global中梳理所有可以访问得到的对象引用,如果达不到,可以达到的就标记未可达到对象,然后循环所有对象,凡是不可达到对象都进行回收,只要没有被引用的变量和方法,都会自带被回收
而一般如果出现内存泄漏了,十有八九是闭包,闭包就是因为内部函数还在被引用,导致外部函数内存无法得到释放的现象。或者就是事件注册了,未清除掉,比如socket、emitter注册的事件,注册完之后不用了未清除掉
什么时候需要使用闭包
ES5时为了实现作用域会用到,ES6基本很少用,但在Class里想要实现私有数据,可以在constructor里面写方法赋值出去,但是这样做,十分需要注意的是,如果new出来的对象不要了,还是给变量弄个null,不然就导致内存泄漏了
process
process.env获取环境,比较常用,还有就是process.cwd获取当前的工作目录,好通过这个目录的相对目录来操作文件之类的,当然还有process.chdir()
改变当前工作目录,还有有时想改变事件队列顺序插入事件,可以用process.nextTick
process里还有像C++里一样的输入输出流,但是很少用,console的底层就是用这个实现的
process.nextTick
process.nextTick会将事件插入到当前事件队列和下轮事件队列之间
而timeout和immediate会放到下轮事件尾
process.nextTick(),效率最高,消费资源小,但会阻塞CPU的后续调用;
setTimeout(),精确度不高,可能有延迟执行的情况发生,且因为动用了红黑树,所以消耗资源大;
setImmediate(),消耗的资源小,也不会造成阻塞,但效率也是最低的。
守护进程
我们公司是用pm2做的守护进程,基本上就是一个配置。而内部实现原理的话,好像都是通过C语言或什么其他语言模块实现的
Child Process
通常如果需要执行一个cmd命令,或者开启线程和执行其他语言的话,可以用这个child_process
之前有写过一个小工具,就用node的process执行cmd命令做adb调试手机。还有用这个执行过dll和go,多是做工具里有用到这个
为什么用node
感觉node的话,其实最开始大家都在宣扬node的高并发,但实际上我觉得选择node的原因,其实并不是怎么并发了
而是其实说,node很简单,写业务很快,同样一个项目,node要比Java快很多
包括说node也很灵活。就拿Java多数据源,好像就很难实现,但node就很简单,connect一下存到一个变量里调里面方法就好了
而且还有个优点就是人员灵活。比如当时做商城的时候,商城不仅需要后端,还需要后台,然后前端人员可能就不够了,我这种前端出身的,帮着写写后台页面是没问题的
KOA和Express
一开始的时候是先出Express,处理异步的方案用的callback,后来出了KOA1,说是面向未来的框架,并且处理异步的方案是* yield next,就开始决定用KOA了,再后来出了2就是用的async、await了。
除了对异步处理之外,KOA的use中间件的方式似乎也和Express不一样,KOA的洋葱模型,每个request会走到每个中间件的next之前,然后每个response都会先走next之后的函数,再response出去
KOA2 用起来 总体的缺点来说的话
功能单薄,需要自己去筛选和选择中间件
而且因为十分开放,开发自由度大,规范不统一
对egg的理解
对于平时写代码,egg利用KOA的中间件机制和HTTP服务机制,对KOA做了很大的扩展
以 Loader 机制作为Egg.js各分层机制的约定基础
Egg还提供了很多功能
比如之前我们公司用KOA,就自己写了初始化的小工具,但egg就已经自带了 脚手架初始化、
同时我们用pm2做的多线程和多线程守护,egg都自带了,所以其实感觉egg就是帮你在灵活的KOA里面定制好了很多标准插件、工具
egg流程
开启插件,先npm i ,
然后再config/plugin里面注册一下,就export.xx= package和enable写一下
之后再config里注册一下
Controller 一般不会自己产出数据,也不会包含复杂的逻辑,复杂的过程应抽象为业务逻辑层 [Service]
this.ctx.curl可以调接口
this.config.可以拿到配置信息
编写扩展
方法扩展
app/extend/application.js
module.exports = {
foo(param) {
// this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
},
};
application里export出来的东西会挂载到app里
属性扩展
一般来说属性的计算只需要进行一次,那么一定要实现缓存,否则在多次访问属性时会计算多次,这样会降低应用性能。
推荐的方式是使用 Symbol + Getter 的模式。
// app/extend/application.js
const BAR = Symbol('Application#bar');
module.exports = {
get bar() {
// this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
if (!this[BAR]) {
this[BAR] = 调接口
}
return this[BAR];
},
}
app/extend/xx.js
export一个function,这个function就可以
request拓展 app/extend/request.js 的东西会合到prototype
response同理 app/extend/response.js
ctx.helper可以 app/extend/helper.js
app/extend/application.unittest.js只会在unittest 环境加载
第三方登录
目前只做了两个第三方登录,分别是QQ和微信的,而且感觉前端看来都是扫码,而后端看来却是两个不同的逻辑
微信接入之后,用户点击会进入回调url并且带一个code参数,微信有提供一个接口可以根据appid、secret、code来获取unionid,之后我会拿这个unionid到数据库里查,这个用户是否存在,
存在的话就直接token返给前端,
不存在的话,我这边会先临时建立一个无姓名、手机号的账户,并把这个账户的unionid给前端,前端跳绑定手机号页面,绑定成功之后我再更新这个用户信息
QQ和微信第三方登录原理差不多,但是QQ登录之后,并没有在query里放code,而是在hash里,后端拿不到hash值,所以我这边回调地址会额外跳转一次,再拿里面的access_token去获取qq的openid,之后的步骤就和微信那边的一样了
核销优惠券
其实这个优惠券的话,其实是用户先在苏宁、天猫、顺逛买了装修优惠券,之后装修付款可以使用这个优惠券。在逻辑里,实际上是用户购买完优惠券之后,有个电子码,输入这个电子码获取优惠。而我们这边会调他们的第三方接口,来获取是否有效、抵扣多少元。如果有效的话,再调第三方接口把这个优惠券置为已使用,并且真正收钱的时候少收一些。这个电子码调用他们第三方接口的时候,因为安全性很重要,所以信息里有电子码、店铺号、秘钥和MD5加密进行校验的
加密
之前这个优惠券的加密方式可能比较low,就第三方平台给我们一个秘钥和店铺号,有点类似微信的那个,然后调用接口的时候,我们要把所有参数按ascii码排序,最后加上店铺号,然后用md5以秘钥进行加密传给他们。他们那边在进行校验我们传输的值是否正确
数据打通
签订合同
签订合同是公司购买了上上签的服务,就是我们先有个合同,用户可以在canvas里签字,