一个关于 npm install 的安全问题

最近发现一个模块  pre-commit  https://www.npmjs.com/package/pre-commit

这个模块的用途是在git commit的时候可以运行一些命令。本质上是在.git/hooks/pre-commit 插入一些命令。

问题来了:为什么  npm install –save-dev pre-commit 具有这么大的权限?能够对非./node_modules/pre-commit 以外的文件做修改?如果真能修改是不是可以修改windows文件了?这样会有安全问题。

关键点在于 npm install 这个命令,自己还不够熟悉,要持续观察。以后找到原因再更新文章。

SLAM 导航小车的持续研究

     对于智能硬件的研究,在2014年的时候就开始投入了,也不知道是为啥,无论为了前景还是兴趣,作为前端工程师的我,好像跟这个硬件关系不大,纯粹就是觉得好玩。对那些比如四轴飞机,固定翼飞机,机器人等等非常好奇,好奇他们是怎么做到的,可能对于机械自动化专业的人觉得挺简单的,我自己也开始折腾的生涯。

     对于网上一大把的智能小车,手机蓝牙控制的,几年前已经玩过了,手机控制小车,51单片机,蓝牙模块与用过了,觉得51单片机太难入门,于是换成了树莓派。

     之后参加了一场黑客马拉松,认识了一些懂硬件的小伙伴,认识了arduino,于是把arduino和树莓派都装到了小车上。

     后来有一段时间迷上了固定翼飞机(FPV),买了一个遥控器和电池回来,后来一个电机,装上。有一次电机失控,螺旋桨直接飞了出来,幸好当时没人在附近,但是由于太危险了,就放弃玩这个了。

     后来发现,蓝牙控制小车距离太短,而且感觉有点low,于是我把买给飞机用的遥控器和电池组都移植到了小车上,这样我的遥控车就有了超长距离的遥控(几公里吧),和十足的动力(900元的4S电池)。

     有了超远距离的实时控制还不够,其实我还需要想,能不能在公司,就能控制家里的小车,这其实是一件非常非常难的事情。wifi是有延迟的,稍微一点的网络延迟几百毫秒,车子就撞墙了,信号就丢了,再加上上面有实时摄像头图像传输,那一定会更加慢了。这都是经过我实际躺坑总结出来的经验。

     于是乎,我在想,与其实时控制,那还不如实时定位。先画出一副家里的地图,鼠标点到哪里,车子就自动走到哪里,像玩梦幻西游,点击NPC就会自动走路过去的效果。于是乎。。。这里涉及到的问题,太多太深奥了。

      第一步,要先画出一副家里的地图(这就是这篇文章要探讨内容)

      第二步,让机器识别当前位置是在地图的哪一个位置

      第三步,鼠标点一下地图,用算法自动导航到指定坐标。

      第四步,没电了,是不是应该会自动充电?

      第五步,是不是能够自动跟随某个东西,比如人,宠物?

      第六步,是不是给它整一个机械臂,做一些简单的拿拖鞋之类的事情?

      哈哈,太多了,其中设计到的硬件知识,电子知识,机械知识,软件知识,图像处理,人工智能。而且。。还要花很多钱买设备。以后的事情以后再考虑吧。眼前我只能先踏出第一步。

      好了,故事说到这里,下面是干货。

      建立地图。可以理解为,自己做一个拥有导航功能的扫地机器人。

      硬件准备

            1. 一台智能小车,假设你已经对它非常熟悉。

            2. rpladir A1 激光测距摄像头(2016年买的,这东西我花了900大洋,现在有更贵的版本了)

            3. 树莓派3

            4. 台式机Ubuntu系统

  

babel插件编写

    之前项目上遇到一个问题,服务器请求api回来的对象如果位空的时候是{list:null, name:null}, 我们期望的值是{list:[], name:""} ,但是后台由于某些原因并没有帮我们做到过滤。于是我们在敲业务代码的时候,经常由于数据问题,导致整个页面挂掉。比如

    let obj ={list:null, name:null}; //这是服务器返回的,不可控
        obj.list.map(()=>{}) //list为null 报错
        obj.name.length //name为null 报错

        虽然说可以在使用之前调用以下判断代码,确保安全

        obj.list = obj.list===null?[] || obj.list
        obj.name = obj.name===null? "" || obj.name;

    但是人为代码难免遗漏,数据难预测,多层嵌套等原因,很难杜绝这种问题。于是有一天突然想到能不能用babel插件彻底解决这个问题呢?

    比如在运行

    function hello(){
        obj.list = obj.list===null?[] || obj.list;   //编译的时候,babel插件帮我加上这句话
        return obj.list.map(v=>{
        }) ;
    }

    这样就几乎能杜绝这个问题。

    于是边开始了这个课题:babel插件编写

    开始之前先看看资料

    

    从零开始写babel插件

    http://eux.baidu.com/blog/2017/12/how-to-write-babel-plugin?utm_source=tuicool&utm_medium=referral

    Babel插件开发指南

    https://github.com/brigand/babel-plugin-handbook/tree/master/translations/zh-Hans

    AST 辅助工具

    http://astexplorer.net/

    我开发的例子

    https://github.com/gaxking/babel-plugin-gax-killnull

    上手要点:

    1. 一定要在 node_modules 编写插件

    2. plugin 和 preset 的排序问题

  • Plugin 会运行在 Preset 之前。

  • Plugin 会从第一个开始顺序执行。

  • Preset 的顺序则刚好相反(从最后一个逆序执行)。

    3. 多尝试http://astexplorer.net/,了解AST语法构造,因为截取代码和插入代码都需要它的知识

    

关于JS小数运算不精确的探讨

各位老司机应该知道,很多时候做小数运算的时候会出现小数溢出。常见的场景是做价格运算,测试经常报bug。

于是针对这个奇特的问题,开始了寻根之旅。

举个例子:0.2+0.1=0.30000000000000004

为什么会这样?

其实很多其他计算机语言都有这个问题。计算机中的所有运算都会经过二进制转换再计算的。

0.2的二进制是  

0.2 * 2 = 0.4 取整数位  0  得 0.0

0.4 * 2 = 0.8 取整数位  0  得 0.00

0.8 * 2 = 1.6 取整数位  1  得 0.001

0.6 * 2 = 1.2 取整数位  1  得 0.0011

0.2 * 2 = 0.4 取整数位  0  得 0.00110

(n)….

太长了 于是直接在控制台上打印 

var num = 0.2;

num.toString(2)

// "0.001100110011001100110011001100110011001100110011001101"

同理算出 0.1的二进制为

// "0.0001100110011001100110011001100110011001100110011001101"

两个二进制相加得出    

// "0.0100110011001100110011001100110011001100110011001100111"

根据这个IEEE754的标准 就会把最后的 1 舍去变成

0.0100110011001100110011001100110011001100110011001101

然后转成十进制得出结果是 0.30000000000000004

再举个例子 0.1+0.3 = 0.4 

为什么这两个数不会产生溢出呢?

算出 0.1的二进制为

// "0.0001100110011001100110011001100110011001100110011001101"

算出 0.4的二进制为

// "0.01100110011001100110011001100110011001100110011001101"

两个二进制相加得出    

// "0.1000000000000000000000000000000000000000000000000000001"

根据这个IEEE754的标准 就会把最后的第52位进一舍零,得到

"0.1000000000000000000000000000000000000000000000000000"

剩下0.1 转换十进制为0.5

找了网上的一张图,其实还不是完全看懂指数位是什么。。。。

QQ图片20171226234411.png

怎么处理?

/**

 * 加法运算,避免数据相加小数点后产生多位数和计算精度损失。

 * 

 * @param a加数1 | b加数2

 */

function Add(a, b) {

    var n, n1, n2;

    try {

        n1 = a.toString().split(".")[1].length;

    } catch (e) {

        n1 = 0;

    }

    try {

        n2 = b.toString().split(".")[1].length;

    } catch (e) {

        n2 = 0;

    }

    n = Math.pow(10, Math.max(n1, n2));

    return (a * n + b * n) / n;

}

/**

 * 减法运算,避免数据相减小数点后产生多位数和计算精度损失。

 * 

 * @param a被减数  |  b减数

 */

function Sub(a, b) {

    var n, n1, n2;

    var precision; // 精度

    try {

        n1 = a.toString().split(".")[1].length;

    } catch (e) {

        n1 = 0;

    }

    try {

        n2 = b.toString().split(".")[1].length;

    } catch (e) {

        n2 = 0;

    }

    baseNum = Math.pow(10, Math.max(n1, n2));

    n = Math.pow(10, Math.max(n1, n2));

    return (a * n – b * n) / n;

}

/**

 * 乘法运算,避免数据相乘小数点后产生多位数和计算精度损失。

 * 

 * @param a被乘数 | b乘数

 */

function Mul(a, b) {

    var n1 = 0,

        n2 = 0;

    try {

        n1 += a.toString().split(".")[1].length;

    } catch (e) {}

    try {

        n2 += b.toString().split(".")[1].length;

    } catch (e) {}

    return (a * Math.pow(10, n1)) * (b * Math.pow(10, n2)) / Math.pow(10, n1 + n2);

}

/**

 * 除法运算,避免数据相除小数点后产生多位数和计算精度损失。

 * 

 * @param a被除数 | b除数

 */

function Div(a, b) {

    var n1 = 0,

        n2 = 0;

    var baseNum3, baseNum4;

    try {

        n1 += a.toString().split(".")[1].length;

    } catch (e) {}

    try {

        n2 += b.toString().split(".")[1].length;

    } catch (e) {}

    return (a * Math.pow(10, n1)) / (b * Math.pow(10, n2)) / Math.pow(10, n1 – n2);

}

// js小数相加,精确的结果应当是10090.701

var numAdd = Add(9856.556, 234.145);

// js小数相减,精确的结果应当是422.441

var numSub = Sub(656.55, 234.109);

// js小数相乘,精确的结果应当是51.285

var numMul = Mul(Mul(34.19, 0.03), 50);

// js小数相除,精确的结果应当是40.0936

var numDiv = Div(100.234, 2.5);

思想是计算出有几位小数(n),先转化成整数,保证用整数进行计算,再除以 10*n; 

从React diff算法原理中得到的React编写优化方案

    讲起React,当然不能缺少它最重要的特性虚拟Dom,但是很多人都讲不清楚虚拟Dom的内在原理,其实关键点还是这Diff算法。

    

    Diff算法说简单的说就是遍历二叉树,render前和render后的每一个节点做匹配。这里说的节点就是DOM节点但是不是真的DOM节点,而是一个对象类似于

    {

        tagname:div

        className:"a",

        style:"width:100px",

        onClick:function(){}

        children:[

            {

                    tagname:span,

                    className:"b",

                    text:"hello",

                    key:1

            },{

                    tagname:span,

                    className:"c",

                    text:"world",

                    key:2

            }

        ]

    }

    对应的DOM如下:

    <div class="a" style="width:100px" onclick="">

        <span class="b"></span>

        <span class="c"></span>

    </div>

    根据二叉树的遍历,从上到下,从左到右遍历,一边遍历,一遍跟旧节点对比。

    1.如果一些基本属性如class,style,text变化了(注意不包括id,key,children),则只修改对应DOM的 class,style,innerText

    2.如果tagname变化了,那就整个元素删掉,重新creatElement('xx')新的标签。这时候原来的childrem都不会再遍历。

    3.如果对象中有key,而且相对于同级元素唯一,那么对于children数组操作的时候,会按key找到元素,修改对应的值。这样的优势是,如果在children中插入一个新的兄弟元素,比如插入头部,它就会判断children[0]的key,跟新插入的是否一致,如果不一致,则创建新元素,然后匹配下一个兄弟元素,并检索是否在旧的childrem是否存在同样id的节点,如果存在,而且被修改过值,则像上面的 1 一样操作。加key优势是不必全量销毁和创建同级DOM。盗一张图类似这样 

WechatIMG101.jpeg

  1. 加key,只对同级别元素有效

  2. 不要变标签,最好能加css隐藏掉

    {if isOk == true?<div>1</div>:<span>1</span>}像这样的写法需要经过销毁和创建DOM的过程,如果换成

    <div style={{display:isOK?block:none}}>1</div>:<span style={{display:isOK?inline:none}}>1</span>,这样就不会频繁销毁和创建

  3. boolean shouldComponentUpdate(object nextProps, object nextState)

    如果很有把握这次render可以不进行,就可以用这个方法对比props和state,return false;阻止render进行.

SLAM小车研究笔记

树莓派与arduino之间通信会偶尔丢包~~怎么解决啊啊啊

对元素定位细节的重新认识

容器和容器内(position:relative)的定位元素(position:absolute)

我们都认为定位元素left,top相对于容器的左上角,但是左上角这个概念很模糊,因为容器和定位元素他们都有盒子模型。下面做个测试

[demo]

结论:定位元素到容器的距离是定位元素的外边距(margin的左上角)到容器的内边距的左上角(内边框)的距离。

HTTPS工作原理

通俗一点,HTTPS就是在HTTP上面加一个SSL,SSL是个什么,到底怎么加?公钥私钥是怎么产生,怎么运作的?相信很多前端都是对他只停留在一知半解的水平。现在针对一下这几个问题写个blog分析一下。

  1. 什么是公钥,什么是私钥。

  2. HTTPS在用户敲完地址之后按回车之后的流程,跟http有啥区别?

  3. HTTPS所需要的证书怎么申请。

  1. 关于公钥,私钥:

        一、公钥加密 
假设一下,我找了两个数字,一个是1,一个是2。我喜欢2这个数字,就保留起来,不告诉你们(私钥),然后我告诉大家,1是我的公钥。

        我有一个文件,不能让别人看,我就用1加密了。别人找到了这个文件,但是他不知道2就是解密的私钥啊,所以他解不开,只有我可以用
数字2,就是我的私钥,来解密。这样我就可以保护数据了。

        我的好朋友x用我的公钥1加密了字符a,加密后成了b,放在网上。别人偷到了这个文件,但是别人解不开,因为别人不知道2就是我的私钥,
只有我才能解密,解密后就得到a。这样,我们就可以传送加密的数据了。

        
 

        二、私钥签名
如果我用私钥加密一段数据(当然只有我可以用私钥加密,因为只有我知道2是我的私钥),结果所有的人都看到我的内容了,因为他们都知
道我的公钥是1,那么这种加密有什么用处呢?

        但是我的好朋友x说有人冒充我给他发信。怎么办呢?我把我要发的信,内容是c,用我的私钥2,加密,加密后的内容是d,发给x,再告诉他
解密看是不是c。他用我的公钥1解密,发现果然是c。
这个时候,他会想到,能够用我的公钥解密的数据,必然是用我的私钥加的密。只有我知道我得私钥,因此他就可以确认确实是我发的东西。
这样我们就能确认发送方身份了。这个过程叫做数字签名。当然具体的过程要稍微复杂一些。用私钥来加密数据,用途就是数字签名。

         

        总结:公钥和私钥是成对的,它们互相解密。

        公钥加密,私钥解密。

        私钥数字签名,公钥验证

  2.  一般大网站在用户敲了http://www.xxxxx.com 之后web服务器会重定向到301,告诉浏览器到跳转到https://wwww.xxxxxx.com

      然后流程如下:

QQ20161108-0.png

        

3 ca证书怎么申请?

    淘宝有卖。。。。

Scoped CSS 研究

看了徐飞大神对组件化的探索

https://github.com/xufei/blog/issues/22,里面提到Scoped CSS,于是顺便研究一下

在chrome和safair都试了几次,都无效果。查了一下caniuse,发现兼容性太差。。。放弃不研究

blob.png

http头缓存相关详细总结

http 请求会返还头部信息和主题内容,头部信息分为General,Pesponse, Request:举个例子

blob.png

包含请求地址,方法,状态码,地址:端口

blob.png

包含跨域白名单,连接方式,压缩方式,返回格式,日期,Keep-Alive,还有些服务器相关的东西

blob.png

包含请求格式,接受哪种压缩方式,哪种语言,Cache控制,链接方式,host,Pragme,User-Agent等

这里先着重研究一下keep-alive到底是什么鬼,它对页面打开速度有影响吗?对服务器会增加压力吗?看来需要新开一片文章来说了,暂时放着。

那么,Pargma,与Cache-Control,还有Expires,Last-Modified这些到底是什么关系,相互有什么区别,优先级怎么处理。

blob.png

Pragma 

网上搜了一圈貌似Pragma只有一个值no-cache,用来禁止缓存的

Expires

告诉浏览器过期时间,缺点是需要跟客户端时间匹配,客户端时间不可靠。

[demo0]

Cache-Control 这个功能优点强大,但我们常见的只是用max-age

Cache-Control 提供了5种属性;那么如果同时设置:

Cache-Control :no-cahce,max-age:600; 那到底是缓存还是不缓存?

做个实验试试:

header("Cache-Control:max-age=600,no-cache"); 

[demo1]

缓存成功

header("Cache-Control:no-cache,max-age=600"); //位置换一下

[demo2]

缓存成功

结论:no-cache优先级比max-age 高,与顺序无关;

Last-Modified : 服务器告诉浏览器这个文件的修改时间。

If-Modified-Since:浏览器告诉服务器这个文件的修改时间

Last-Modified 和 If-Modified-Since必须要一起使用。

[demo3]

Etag:服务器给文件生成的唯一标识。

If-None-Match: 浏览器告诉服务器这个文件的标识。

[demo4]

那么既然有5种方法控制缓存,他们都加上的话,优先级又是怎样呢?

Pragma,Expires,Cache-Control ,Last-Modified ,Etag

实验1:

blob.png

[demo5]

结论:每次都请求服务器,证明Pragma生效,所以优先级比其他都高。

那么剩下的Expires,Cache-Control ,Last-Modified ,Etag 优先级又如何?

先比较 Expires,Cache-Control

blob.png

[demo6]

结论:每次都请求服务器,证明Cache-Control生效,所以优先级比Expires高。

再比较 Last-Modified ,Etag 

blob.png

[demo7]

结论:按f5的时候,都请求服务器,同时带上If-None-Match,If-Modified-Since,究竟会不会返回304,这是服务器说了算。所以Last-Modified 和Etag比较是没有意义的。 

然后将两组优先级高的提取出来,Last-Modified和Etag就把他们当中一个东西吧。 

Cache-Control  VS Last-Modified/Etag 

blob.png

结论:每次按地址栏回车都会请求服务器,证明Cache-Control优先级更高。

剩下 Expires VS ast-Modified/Etag 

blob.png

结论:在Expries过期的情况下,每次按地址栏回车,都会请求,证明Expires优先级更高。

最后来个总的排名: Pragma>Cache-Control>Expires>Last-Modified||Etag

操作行为对资源缓存的影响:

blob.png

按Ctrl+F5就没啥好说的啦,强制刷新,其实就是If-None-Match,If-Modified-Since都不会带上,导致服务返回200;

按F5的话其实都会请求服务器,但是如果有Last-Modified/Etag,那么就会带上If-None-Match/If-Modified-Since。服务器判断是否返回304

其他操作行为都不会请求服务器

最终结论,如果Expires,Cache-Control ,Last-Modified ,Etag存在,那么以Cache-Control 为准。