内容安全策略( CSP )

CSP 通俗的来说就是可以根据header的字段,或者meta标签,限制页面的script,图片,video等外部自由的加载来源,以达到防止被XSS的目的

下面来实践一下:

header 可以设置:Content-Security-Policy

或者在

meta 设置 <meta http-equiv="Content-Security-Policy" content="default-src 'self'">

这里方便我做测试,就使用meta方式吧

常用设置如下:

1.所有内容均来自站点的同一个源

Content-Security-Policy: default-src 'self'  

[demo]

2.允许所有内容来自信任的域名,或指定域名

Content-Security-Policy: default-src 'self' *.baidu.com

[demo]

3.分别控制网页的图片,音频或视频,脚本

Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src luoyongjie.cn

[demo]

4.  Content-Security-Policy-Report-Only: /something/

这个属性只会产生警告不强制失败

[demo]

5.还能发送违法请求

Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.cgi

这里就不写demo了

常用图片格式对比分析

常用图片无非就以下4种

  1. PNG

  2. JPG(JPEG)

  3. GIF

  4. WebP

1.png分为png8 和 png24

png8 只支持2^8(256)种颜色,半透明和全透明

仔细看一下png8的妹子图片,颜色看起来确实不够鲜艳

image.png

这幅是我随便画的图片,图层设置50%半透明,但是png8 只支持 全透明和全不透明

image.png

png8 适合应用于颜色比较单一的logo,icon图片。

png24相比,支持半透明,色彩也显示得更加丰富,但是相应的图片大小更大,适合应用于摄影作品

jpg也可以叫jpeg,色彩支持24bit,同png24一样能显示丰富的色彩,但是不支持半透明,但是图片体积比较小,适用于网络上显示比较丰富的图片


在图片经过编辑保存之后,jpg图片会逐渐失真,png不会。

原因是jpg采用的是有损压缩,png采用的是无损压缩。


gif:gif可以支持动画,类似于png8的布尔透明类型,只有全透明跟全不透明,没有半透明,是无损耗的图像格式。

webP:是有损压缩,

WebP:可以选择区别无损和有损压缩

在有损压缩情况下,相比JPEG减少25%~34%的大小,有损WebP也支持透明通道,大小通常约为对应PNG的1/3

在无损压缩情况下,相比PNG减少26%大小

于gif相比支持动画,动态WebP相比GIF支持更丰富的色彩,并且也占用更小空间,更适应移动网络的动图播放。

1539156399134726.png

唯一的缺点是,技术实现的成本比较高,而且苹果和ie对它完全不支持

​《你不知道的Javascript-中册》阅读笔记

第一部分

第一章

1、

var a;

a; // undefined

b; // ReferenceError: b is not defined

undefined 和 is not defined 是两回事

2.

// 这样会抛出错误

if(DEBUG) {

    console.log( "Debugger is starting" )

}

//这样是安全的

if (typeof DEBUG !== "undefined") {

    console.log( "Debugging is starting" )

}

第二章

1.

var b = 42.;  //42.这样的写法没问题,只是不常见,但从代码的可读性考虑,不建议这样写。

42.toFixed(3) 是无效语法,因为 . 被视为常量42.的一部分 , 42..toFixed(3) 则没问题

2.

Object.is() 主要是用来处理那些特殊的相等比较 

Object.is(NaN, NaN) //true

Object.is(0, -0) //true

3.

对包含循环引用的对象执行JSON.stringify(..) 会出错

第4章

1.

以下这些是假值:

undefined

null

false

+0, -0 和NaN

""

假值的布尔强制类型转换结果为false

理论上 假值列表以外的都应该是真值。  

2.

使用 Date.now() 来获取当前时间戳,比new Date().getTime() 简单

3.

 ~x 大致等于 -(x+1) 

~42; // -(42+1) ==> -43

4.

var a = "Hello World";

if(a.indexOf("ol") == -1){

    //没有匹配

}

这里是指用-1作为失败时的返回值,这些细节应该被屏蔽。

var a= "Hello World";

~a.indexOf("lo"); // -4 真值

if (~a.indexOf("of")) {  //true

    //找到匹配

}

~a.indexOf("ol"); //  0 <–假值

!~a.indexOf("ol"); // true

if(!~a.indexOf("ol")){ //true

    //没有匹配!

}  

5.

~~49.6

~~x 能对小数进行截取,取代Math.floor(xxx),但需要注意负数的情况

~~-49.6 // -49,    ~~中的第一个~执行ToInt32并反转字位,然后第二个~再进行一次字位反转,即将所有字位反转回原值,最后得到仍然是ToInt32的结果

Math.floor( -49.6 ) //-50

6.

[] + {} //[Object object]   "" + [Object object] 

{} + [] //0     {}被当作一个独立的空代码快,不执行任何操作。  代码块结尾不需要分号, 最后+[] 将[]显示强制类型转换

7.

true || false && false;  //true
(true || false) && false;  //false 
true || (false && false);  //true

这说明&&运算符优先于||执行

8.

function foo( a = 42, b = a+1) {
    console.log(
        arguments.length, a, b,
        arguments[0], arguments[1]
    )
}
foo();                             //0 42 43 undefined undefined
foo( 10 );                       //1 10 11 10 undefined
foo( 10, undefined );    //2 10 11 10 undefined
foo( 10, null );              //2 10 null 10 null

在ES6中,虽然参数a和b都有默认值,但是函数不带参数时,arguments数组为空.

如果向函数传递undefined值,则arguments数组中会出现一个值为undefined的单元

9.

<script>foo();</script>

<script>

    function foo() { .. }

</script>

报错,不会进行变量提升

10.

在调试的过程中,有可能对象在console.log(…)语句之后被修改,看到了意料之外的结果,要意识到 这可能是I/O的异步造成的,可以用JSON.stringify()快照一次

第四章

1.Promise中,多次调用reject或resovles,只有第一次有效

new Promise((resovles, reject)=>{
        //something
        resovies(); //只有第一次有效
        resovies(); //无效
})

12.Promise中,只能返回第一个参数

new Promise((resovles, reject)=>{
        //something
        resovies(a, b, c); //只返回a
})

13. 如果Promies中发生错误,则不往下执行,而是返回一个promise,包含错误信息

var p = new Promise(()=>{xxxx});
var p2 = p.then(()=>{
    make some error code
    //不会报错,也不会运行到下一行
    some code
},()=>{
    //不会执行到这里
})
p2.then((e)=>{
    console.log(e) //e就算错误信息
})

14. Promise的局限性有6个

    1.Promise链中的错误信息很容易被无意中默默忽略掉

    2.只接受单一值返回

    3.单决议 // 就是 resolve或rejuect只能运行一次

    4.惯性 //就是通常要封装request之类的

    5.无法被取消

    6.性能稍微低一点点

15. 

function *something(){
    while(true){
        console.log(xxxx)    
    }
    finally{
        console.log("cleaning up!")
    }
} 
var it = something();
for(var v of it) {
    console.log(v);
    if(v > 500){
        it.return("end").value;
    }
}

调用it.return(…)之后,它会立即终止生成器,运行finall语句

16.

function *foo(){
    console.log( "*foo() starting" );
    yield 3;
    yield 4;
    console.log( "*foo() finished");
}
function *bar() {
    yield 1;
    yield 2;
    yield *foo(); // yield委托
    yield 5;
}
var it = bar();
it.next().value;   //1
it.next().value;   //2
it.next().value;   //*foo() starting   注意观察这里,*bar() 把自己的迭代控制委托给了*foo()
                      //3
it.next().value;   //4
it.next().value;   //*foo() finished
                   // 5

第五章

1.尾调用优化

function bar(y){
    return foo( y + 1);  //尾调用
}
function baz(){
    return 1 + bar( 40 );  //非尾调用
}

《你不知道的Javascript-上册》阅读笔记

第一部分  作用域和闭包

第三章

 1.

{
    console.log( bar ); //Error
    let bar = 2;
}

说明1. let 函数没有变量提升这个说法, 

       2. 在大括号内就能产生块作用域

第四章

1.

foo(); //error
{
function foo() {console.log("b")}
}

说明函数的提升只在自己的块级作用域有效

console.log(a) //undefined 注意不是error
{
var a;
}

说明var的声明提升可以在子级的块级作用域有效

2.

JavaScript中的作用域就是词法作用域

function foo() {
  console.log(a); //2
}
function bar() {
  var a = 3;
  foo();
}
var a = 2;
bar();

3.

var fun = function foo() {console.log("b")}
fun();  //b
foo(); // error

说明同时使用2个变量命名的函数,会忽略后面一个

第二部分 this解析

当判断this的真实对象时,需要应用下面四条规则中的其中一条:

  1. 默认绑定

     this 一般指向全局对象,严格模式是undefined

      function foo(){
            console.log(this.a);  //use strict 只对当前块级作用域有效,所以这里不是undefined
     }
    var a = 2;
    
    (function(){
        "use strict";
        foo();//2
    })();

    foo() 是直接使用不带任何修饰的函数进行调用的,因此属于默认绑定

    2.隐式绑定

    obj.foo()  //类似这种有上下文调用的

    3.显示调用

     call,apply,bind

    4. new 一个实例

2. 

function foo() {
    console.log(this.a);
}
var a = 2;
foo.call( null );//2

如果call,apply,bind传入null,则应用默认绑定

3.

function foo()
{
    return (a)=> {
    console.log(this.a)
    }
}
 
var obj1 = {
    a:2
}
 
var obj2 = {
    a:3
}
 
var bar = foo.call(obj1);
bar.call(obj2); //2

foo()调用一次时,箭头函数中的this获得 obj1 , 由于箭头函数一旦获得this,就无法被修改,所以还是obj1

 第三章 对象

  1. 深度复制对象时,要注意循环引用的问题

第五章 原型

  1. 判断两个对象之间的关系,可以用

 b.isPrototypeOf( c );

2. 使用Class的一个缺点:

class C{
    constructor(){
        C.prototype.count++
        console.log("Hello:" + this.count);
    }
}
C.prototype.count = 0;
var c1 = new C();
//Hello:1
var c2 = new C();
//Hello:2
c1.count === 2;//trte
c1.count === c2.count;//true

这种方法问题是,它违背了class语法的本意。在实现中暴露了.prototype

如果使用 this.count++ 我们会发现 c1,c2上都创建了 .count属性,而且互相共享

《ES6 标准入门》 阅读笔记

第三章

    1.var {foo:baz} = {foo:"aaa", bar:"bbb"}   //bas = bbb, foo = undefined

    对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

    

    2.嵌套赋值的例子,平时开发经常会用到

    let obj = {};

    let arr = [];

    ({foo:obj.prop, bar:arr[0]}) = {foo:123, bar:true};

    obj //{prop:123}

    arr //[true]

   3.只要等号右边的值不是对象,就先将其转为对象。

    let {toString:s} = 123;

    s === Number.prototype.toString //true

    

    let {toString:s} = true

    s === Boolean.prototype.toString //true

    4. 交换变量

    es6 :[x, y] = [y, x];

    想到一种es5的写法 x = [y, y = x][0]

第五章

    1.正则表达式的  y修饰符,y修饰符隐含了头部匹配的标志

    var s = "aaa_aa_a";

    var r1 = /a+/g;

    var r2 = /a+/y;

    r1.exec(s) //["aaa"]

    r2.exec(s) //["aaa"]

    r1.exec(s) //["aa"]

    r2.exec(s) //null

    var s = "aaa_aa_a";

    var r = /a+_/y   //换一下表达式

    r.exec(s) //["aaa_"]

    r.exec(s) //["aa_"]

第七章

    1.Array.from() 作用是把类数组的对象转化为真正的数组, 适用于(arguments对象,NodeList,Iterable对象,Set,Map)

    let arr = {'0':'a', '1':'b', '2':c, length:3};

    ES5:

    var arr1 = [].slice.call(arr);

    ES6

    let arr2 = Array.from(arr);

    2.Array.includes()

    [1,2,3].includes(2) //true 方法属于es7

    3.数组推导,可以在[]内用循环创建数组

    var years = [1,2,3,4,5,6,7];

    [for (year of years) if(year>3)]   //[4,5,6,7] 

第八章

    1.

    function m1({x = 0, y = 0}){

        return [x, y];

    }

    function m2({x, y} = {x:0, y:0}){

        return [x,y];

    }

继续阅读

一个关于 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 导航小车的持续研究

     1532620977586210.png

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

继续阅读

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插件彻底解决这个问题呢?(其实babel不是最好的方法,这里是针对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

继续阅读

从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进行.