GithubHelp home page GithubHelp logo

blog's Introduction

blog's People

Contributors

zyjoey avatar

Watchers

 avatar  avatar

blog's Issues

从功能级到工程级代码的思考

最近组里给我安排了个新任务:写一个通用的上传文件至云端的接口。

原本我还挺不解,原因是这个需求只是从某个后台(基于EGG实现)衍生出来的罢了,写到后台项目里半天时间就能搞定~然而老大的意思是上传的逻辑得从原项目里分离成独立的项目,方便以后其它的项目直接调用。

于是原本半天的工作量花了两三天才调通,做完后喜滋滋地交给老大看~老大把我代码一down下来扫了几眼就开始批判说怎么连环境变量也没有,整个一句node index就跑起来了,端口号也是写死的,部到线上端口号被占了这怎么运行?日志呢?出了问题怎么查?

问得我一阵懵,开始还反驳两句说端口号被占我改代码就是= =#老大说这些都应该做成可配置项而不是去修改你的代码,换个人来部署是不是就得从头到尾把你的代码看一遍?

满腹委屈被打回来修改代码,老大还说让我改完之后自己部到测服上跑一遍再说。

然而当我着手开始修改这些问题时,我才意识到我写的只是功能,离一个工程还远得很,我甚至连如何守护进程也不知道。

在将代码逐步从一个文件(是的!!我全写到一个文件里了!!!因为代码一共也才一百多行~!!)分离出来的过程中,我开始理解老大的话,也越来越理解所谓工程级项目为何需要将代码细分至此,强调规范和灵活的可配置性~

虽然初始的代码量很少,我也尽量将其划分细致,后续再修改也能清晰许多~
另一个头疼的问题还有部署,之前做的要么是纯前端项目,要么是已经有其他人部署好,只需按照README上写好的流程执行一下脚本就成~ 这次连脚本都得自己写,还有进程守护~与本地开发不同,启动服务后ctrl+c或关闭终端服务也就结束了,一切信息都是直接输出在终端上,而部署到服务器上是需要将进程在后台挂起,终端不会输出任何信息,这也是为什么需要日志的原因。

起初我使用的是nohup node index &,然而这样的问题是无法设置环境变量,且无法重启,再次执行就会出现端口号已被占用的错误~ 除非kill [pid]~ 然而[pid]也不是固定的~ 后来查看一些文章之后就使用pm2来守护~ 写好pm2的配置文件妥妥满足需求啦~!

如今回头看我最初版的代码确实很值得吐槽,一点儿也不优雅大气((*´ノ皿`)),凡是想着有问题再改代码就是~ 哪能一次就做到满分的心态而原谅自己的局限性~ 完全没有工程师的思维嘛~!!!

JavaScript变量生命周期:为何let没有“变量提升”?

原文链接

提升是指变量或函数挪到作用域起始位置的处理步骤,通常变量声明使用var语句,函数声明则是function fun {...}

let声明(constclasslet声明行为类似)在ES2015标准中问世时,包括我在内的许多开发者都曾使用提升定义来描述如何访问变量。但后来在此问题上进行诸多研究后,我惊讶地发现提升并非描述let初始化和有效性的正确术语。

ES2015为let提供了不同且改良的机制。它要求更严格的变量声明写法(无法在定义前使用),从而获得更优的代码质量。

让我们深入了解这个过程的技术细节吧。

1. 易错的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;
}

变量numvar num声明前即可访问,被赋值为undefined。函数function getPi(){...}在末尾定义,然而它也可以在getPi声明前被调用,正因它被提升到作用域的顶部。

这便是典型的提升。

由此可见,先使用再声明变量或函数可能造成困惑。假设你滚动鼠标浏览一个大文件,突然发现一个未声明的变量……它的出现是多么可怕而它又是在何处被定义?

当然一个有过实践经验的开发者不会这么写,但在成千上万的JavaScript GitHub仓库里是完全可能发生的。

纵然阅读上方的示例,也难以理解代码声明流。

那么自然地,let鼓励你遵循这种方式使用变量:首先声明或描述一个未知项,以后再为它赋值。

2. 引擎之下:变量的生命周期

当引擎处理变量时,它们的生命周期有如下几个阶段:

  1. 声明阶段在作用域注册变量;
  2. 初始化阶段分配内存、为变量在作用域创建绑定。此步骤变量自动初始化为undefined
  3. 赋值阶段为初始化的变量赋值。

一个变量具有未初始化状态,介于声明阶段之后与初始化阶段以前。

image

请留意,根据变量的生命周期,“声明阶段”是不同于广义上“变量声明”这一术语。简言之,引擎处理的变量声明包含三个阶段:声明阶段、初始化阶段和赋值阶段。

3. var变量生命周期

已然知悉生命周期阶段,那么就用它来描述引擎如何处理var变量。
image

设想如下情况:当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

4. 函数声明生命周期

如果是函数声明语句,function funName(){...}就更简单。

image

声明阶段、初始化阶段、赋值阶段全部在函数作用域的起始处立即执行(只有一步)。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

5. let变量生命周期

let变量的处理不同于var。主要区别在于声明阶段与初始化阶段的分割。

image

现在来研究一个场景,当解释器进入含有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使其进入赋值阶段。

constclass类型具有与let相同的生命周期,除了赋值区别,赋值只能发生一次。

5.1 let生命周期里提升无效的原因

如上所言,提升是变量的声明阶段与初始化阶段在作用域顶部的耦合。然而let生命周期将其解耦。解耦消除了let的提升项。

两个阶段间的空隙称为暂时性死区,其变量不可访问。

用科幻语言描述,于let生命周期而言,提升的坍缩造就了一个暂时性死区。

6. 总结

自由地使用var进行变量声明容易出错。在本节内容里,ES2015介绍了let.它采用了改进算法以声明变量并且额外增加了块级作用域。

由于声明阶段和初始化阶段的解耦,提升对let变量(同理constclass)不再有效。在初始化进行前,变量进入暂时性死区并且不可访问。

为了保持变量声明的流畅性,提出以下建议:

  • 先声明与初始化,然后再使用变量。这个流程是准确并易于遵循的。
  • 尽量隐藏变量。变量暴露越少,你的代码越模块化。

月光

昨晚睡觉时,发现月光从窗户照了进来,这是换了新房子后第一次见入窗的月光。或许之前她也进来过,只是被我忽略了,也许是我入睡的时机不对,从未和她碰过面。但这不重要,重要的是我现在感觉到了她的存在,我凝视着这月光,什么也没想,什么也想不起来。

不知道过了多久,我发现这月光和以前的月光并没有区别。

海上月是天上月,眼前人是心上人。

记一次由emoji引起的事故

今天上内网运营模板后台发现首页报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;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.