(原先的博客站点被我关闭了~ 因为没有提issue方便,不过代码还是有备份,可以在tags1.1查看~)
zyjoey / blog Goto Github PK
View Code? Open in Web Editor NEW技术笔记
技术笔记
(原先的博客站点被我关闭了~ 因为没有提issue方便,不过代码还是有备份,可以在tags1.1查看~)
最近组里给我安排了个新任务:写一个通用的上传文件至云端的接口。
原本我还挺不解,原因是这个需求只是从某个后台(基于EGG实现)衍生出来的罢了,写到后台项目里半天时间就能搞定~然而老大的意思是上传的逻辑得从原项目里分离成独立的项目,方便以后其它的项目直接调用。
于是原本半天的工作量花了两三天才调通,做完后喜滋滋地交给老大看~老大把我代码一down下来扫了几眼就开始批判说怎么连环境变量也没有,整个一句node index
就跑起来了,端口号也是写死的,部到线上端口号被占了这怎么运行?日志呢?出了问题怎么查?
问得我一阵懵,开始还反驳两句说端口号被占我改代码就是= =#老大说这些都应该做成可配置项而不是去修改你的代码,换个人来部署是不是就得从头到尾把你的代码看一遍?
满腹委屈被打回来修改代码,老大还说让我改完之后自己部到测服上跑一遍再说。
然而当我着手开始修改这些问题时,我才意识到我写的只是功能,离一个工程还远得很,我甚至连如何守护进程也不知道。
在将代码逐步从一个文件(是的!!我全写到一个文件里了!!!因为代码一共也才一百多行~!!)分离出来的过程中,我开始理解老大的话,也越来越理解所谓工程级项目为何需要将代码细分至此,强调规范和灵活的可配置性~
虽然初始的代码量很少,我也尽量将其划分细致,后续再修改也能清晰许多~
另一个头疼的问题还有部署,之前做的要么是纯前端项目,要么是已经有其他人部署好,只需按照README上写好的流程执行一下脚本就成~ 这次连脚本都得自己写,还有进程守护~与本地开发不同,启动服务后ctrl+c
或关闭终端服务也就结束了,一切信息都是直接输出在终端上,而部署到服务器上是需要将进程在后台挂起,终端不会输出任何信息,这也是为什么需要日志的原因。
起初我使用的是nohup node index &
,然而这样的问题是无法设置环境变量,且无法重启,再次执行就会出现端口号已被占用的错误~ 除非kill [pid]~ 然而[pid]也不是固定的~ 后来查看一些文章之后就使用pm2来守护~ 写好pm2的配置文件妥妥满足需求啦~!
如今回头看我最初版的代码确实很值得吐槽,一点儿也不优雅大气((*´ノ皿`)),凡是想着有问题再改代码就是~ 哪能一次就做到满分的心态而原谅自己的局限性~ 完全没有工程师的思维嘛~!!!
不能评论了~
提升是指变量或函数挪到作用域起始位置的处理步骤,通常变量声明使用var
语句,函数声明则是function fun {...}
。
当let
声明(const
和class
与let
声明行为类似)在ES2015标准中问世时,包括我在内的许多开发者都曾使用提升定义来描述如何访问变量。但后来在此问题上进行诸多研究后,我惊讶地发现提升并非描述let
初始化和有效性的正确术语。
ES2015为let
提供了不同且改良的机制。它要求更严格的变量声明写法(无法在定义前使用),从而获得更优的代码质量。
让我们深入了解这个过程的技术细节吧。
var
提升我时常可见在作用域中,任何地方有关变量声明var varname
和函数声明function funName(){...}
的诡异写法:
// var hoisting
num; // => undefined
var num;
num = 10;
num; // => 10
// function hoisting
getPi; // => function getPi() {...}
getPi(); // => 3.14
function getPi() {
return 3.14;
}
变量num
在var num
声明前即可访问,被赋值为undefined
。函数function getPi(){...}
在末尾定义,然而它也可以在getPi
声明前被调用,正因它被提升到作用域的顶部。
这便是典型的提升。
由此可见,先使用再声明变量或函数可能造成困惑。假设你滚动鼠标浏览一个大文件,突然发现一个未声明的变量……它的出现是多么可怕而它又是在何处被定义?
当然一个有过实践经验的开发者不会这么写,但在成千上万的JavaScript GitHub仓库里是完全可能发生的。
纵然阅读上方的示例,也难以理解代码声明流。
那么自然地,let
鼓励你遵循这种方式使用变量:首先声明或描述一个未知项,以后再为它赋值。
当引擎处理变量时,它们的生命周期有如下几个阶段:
undefined
;一个变量具有未初始化状态,介于声明阶段之后与初始化阶段以前。
请留意,根据变量的生命周期,“声明阶段”是不同于广义上“变量声明”这一术语。简言之,引擎处理的变量声明包含三个阶段:声明阶段、初始化阶段和赋值阶段。
var
变量生命周期已然知悉生命周期阶段,那么就用它来描述引擎如何处理var
变量。
设想如下情况:当JavaScript遇到函数作用域里包含var variable
语句,变量在作用域的顶部、所有语句执行前,经过声明阶段并立刻进入初始化阶段(步骤1)。var variable
语句在函数作用域里的位置并不影响其声明和初始化阶段。
声明和初始化阶段之后,赋值阶段以前,变量已然具有可使用的undefined
值。
在赋值阶段variable = 'value'
,变量接受其初始值(步骤2)。
严格地说提升是指在函数作用域的起始处声明和初始化一个变量。声明与初始化阶段之间没有空隙。
让我们研究这个例子。以下代码创建了一个含有var
语句声明的函数作用域。
function multiplyByTen(number) {
console.log(ten); // => undefined
var ten;
ten = 10;
console.log(ten); // => 10
return number * ten;
}
multiplyByTen(4); // => 40
当JavaScript开始执行multipleByTen(4)
并进入函数作用域,变量ten
在第一条语句以前就经过了声明和初始化步骤。所以当调用console.log(ten)
会输出undefined
。
语句ten = 10
赋一个初始值。赋值以后,console.log(ten)
输出正确的10
。
如果是函数声明语句,function funName(){...}
就更简单。
声明阶段、初始化阶段、赋值阶段全部在函数作用域的起始处立即执行(只有一步)。funName()
不依赖于声明语句的位置(即使是末尾),可以注入在该作用域的任意之处。
以下代码示例揭示函数提升:
function sumArray(array) {
return array.reduce(sum);
function sum(a, b) {
return a + b;
}
}
sumArray([5, 10, 8]); // => 23
当JavaScript执行sumArray([5, 10, 8])
,引擎进入sumArray
函数作用域。内部作用域里,sum
在所有语句执行前立即经历全部三个阶段:声明阶段、初始化阶段和赋值阶段。
array.reduce(sum)
方法能够在function sum(a, b){...}
语句声明前访问sum
。
let
变量生命周期let
变量的处理不同于var
。主要区别在于声明阶段与初始化阶段的分割。
现在来研究一个场景,当解释器进入含有let variable
语句的块级作用域,变量立即经历声明阶段,在该作用域注册了它的名字(步骤1)。
接着解释器依次解析块内的语句。
如果此时你试着访问变量,JavaScript会抛出ReferenceError:variable is not defined.
。因为变量处在未初始化状态,位于暂时性死区。
当解释器抵达let variable
语句,便经历了初始化阶段(步骤2)。现在变量状态处于初始化阶段,访问其结果为undefined
。变量退出暂时性死区。
随后出现语句variable = 'value'
,变量经历赋值阶段(步骤3)。
如果JavaScript遇到let variable = 'value'
,那么初始化与赋值阶段一起执行。
来看以下例子。块级作用域创建一个let
变量number
。
let condition = true;
if (condition) {
// console.log(number); // => Throws ReferenceError
let number;
console.log(number); // => undefined
number = 5;
console.log(number); // => 5
}
当JavaScript进入if (condition) {...}
块,number
立即经历声明阶段。
number
具有未初始化状态因而进入暂时性死区,企图访问变量时将抛出ReferenceError: number is not defined
错误。随后语句let number
使其经历初始化。现在你可以访问它了,不过它的值还是undefined
。
自然而然,赋值语句number = 5
使其进入赋值阶段。
const
与class
类型具有与let
相同的生命周期,除了赋值区别,赋值只能发生一次。
let
生命周期里提升无效的原因如上所言,提升是变量的声明阶段与初始化阶段在作用域顶部的耦合。然而let
生命周期将其解耦。解耦消除了let
的提升项。
两个阶段间的空隙称为暂时性死区,其变量不可访问。
用科幻语言描述,于let
生命周期而言,提升的坍缩造就了一个暂时性死区。
自由地使用var
进行变量声明容易出错。在本节内容里,ES2015介绍了let
.它采用了改进算法以声明变量并且额外增加了块级作用域。
由于声明阶段和初始化阶段的解耦,提升对let
变量(同理const
和class
)不再有效。在初始化进行前,变量进入暂时性死区并且不可访问。
为了保持变量声明的流畅性,提出以下建议:
昨晚睡觉时,发现月光从窗户照了进来,这是换了新房子后第一次见入窗的月光。或许之前她也进来过,只是被我忽略了,也许是我入睡的时机不对,从未和她碰过面。但这不重要,重要的是我现在感觉到了她的存在,我凝视着这月光,什么也没想,什么也想不起来。
不知道过了多久,我发现这月光和以前的月光并没有区别。
海上月是天上月,眼前人是心上人。
今天上内网运营模板后台发现首页报500错误,起初以为是服务器挂掉,本能想要重启服务,然而试着打开该站其他网页却仍然能正常访问,甚至修改一下首页的参数也好好的,于是猜测是由某条记录解析错误导致。查了下日志,日志很精准的指出引起错误的代码行,关停该台机器的服务后,直接用vi在问题代码行添加try{}catch(){},然后发现是数据库存储的某条记录格式不正确~ 询问操作该条记录的运营同学后,她才依稀想起是在标题行带有了emoji表情后提交出错了~ 我在测试环境模拟该操作后果然问题复现了~
查找资料后解释是说数据库通常是以utf8格式存储,而emoji表情是utf16格式,故引起了存储失败,一条解决办法是直接设置数据库的表为utf8mb4。
mb4即most byte4,简言之utf8mb4是utf8的超集并完全兼容utf8,能够用四个字节存储更多的字符。
标准的 UTF-8 字符集编码是可以用 1~4 个字节去编码21位字符,这几乎包含了是世界上所有能看见的语言了。
然而在MySQL里实现的utf8最长使用3个字节,也就是只支持到了 Unicode 中的 基本多文本平面(U+0000至U+FFFF),包含了控制符、拉丁文,中、日、韩等绝大多数国际字符,但并不是所有,最常见的就算现在手机端常用的表情字符 emoji和一些不常用的汉字,如 “墅” ,这些需要四个字节才能编码出来。
另一方式是将其编码存储,例如使用encodeUrlComponent,然而在字段值设置了可存储字节的约束下,这种方法显然很容易导致超出,所以想到的是只将emoji进行unicode转码存储~这里需要用到的正则表达式:
/([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])/
需要注意的是一个emoji的表情的length值是2,所以封装编码函数如下:
function encode(str){
let code = '';
for(let i = 0; i < str.length ; i++){
code = code + '\\u' + str.charCodeAt(i).toString(16);
}
return code;
}
完整代码如下:
class util{
encode(str){
let code = '';
for(let i = 0; i < str.length ; i++){
code = code + '\\u' + str.charCodeAt(i).toString(16);
}
return code;
}
decode(str){
let code = '';
for(let i = 0; i < str.length ; i++){
code = code + '\\u' + str.charAt(i);
}
return code;
}
encodeEmoji(data){
if(typeof data !== 'string'){
return '';
}
const that = this;
return data.replace(/([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])/g,function(match){
return that.encode(match);
});
}
decodeEmoji(data){
if(typeof data !== 'string'){
return '';
}
const that = this;
return data.replace(/([\uE000-\uF8FF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF])/g,function(match){
return that.decode(match);
})
}
}
return util;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.