《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 这个命令,自己还不够熟悉,要持续观察。以后找到原因再更新文章。

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

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

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

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

[demo]

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

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 高,与顺序无关;

继续阅读

关于DOMContentLoaded的触发时机探究

相信很多人都知道DOMContentLoaded,也就是$.ready(),是在页面DOM构建完之后触发的。

今天突然想到一个问题,这个DOM构建完是指HMTL解析器解析成DOM之后算是构建完,还是渲染的最后一步绘图结束后算是构建完。

直接看图吧:

blob.png

也就是DOMContentLoaded是在 (1) 还是(2)阶段触发呢?

[demo],在demo中可以看到黄色的DIV出现在弹框之后;

证明我们常说的DOMContentLoaded,正确的理解是页面首次渲染前触发(发现这个弹框也不准,这个结论还不算正确)

狂啃犀牛书-笔记

第二章

x

++

y

解析为 x;++y;而不是x++;y;

第三章

  1. 只有 null 和 undefined 是无法拥有方法的值。

  2. js能表示的数字范围是(-2^53~2^53), 因为采用IEEE754标准。对于这个标准的来龙去脉。不作深究。

  3. var s = "hello world"; s.charAt(0);//"h"  ,可用charAt(x)获取对应位置上的字符,中文也适用。

    也可用用s[s.length-1];//d

  4. 原始值(undefined,null,布尔值,数字,字符串):任何方法都无法改变原始值;

    var s = "hello world";  

    s[0] = "1"// s不变;

    s.toUpperCase(); //s不变,只是返回新的字符串。

  5. Boolean(-1)//true,只要是非0,就等于true

    Boolean([])//true

    Boolean({})//true

  6. !xx  //等价于 Boolean(x)

  7. 对象到字符串的转换:

    调用toString()->valueOf()->toString()->errror

    对象到数字的转换:

    调用valueOf()->toString()->number()->error

  8. 任何对象在左==或者"<",">"等作比较的时候,都会首先尝试调用valueOf(),判断是否能作比较,如果不能比较然后再调用toString();

    var obj = {};

    obj.toString = function(){return 0};

    false == obj //true

  9. JS 中var变量的块级作用域只存在于函数中,与{}无关。

    function test(){

      var i = 1;

      if(false){

          var j = 2;

      }

     console.log(j);//j==underfine 有定义,没赋值。

    10.当使用var 声名一个变量,创建的这个属性是不可配置的,也就是说这个变量无法被delete;

    11.在一层层作用域链中查找变量的过程叫做“变量解析”

第四章

     1. var x = [1,2,,,] //x = [1,2,undefined] ,无论后面有多少个逗号,都只有一个undefined

     2. 如果一个对象的创建不需要传入任何参数给构造函数的话,那么这对空圆括号是可以省略的; // new Object , new Data

     3. 数字除以0,等于正负无穷大,0除以0等于NaN     // 1/0 = Infinity,    0/0 = NaN

     4. 加号优先考虑字符串链接  // "1" + 1 = 11

         如果其中一个是对象则调用toString()之后相加,  // 1+ {} = 1[Object Object]

         其余情况会将两边转换为数组相加  //true+true = 2,  null + true = 1

     5. "++"运算符从不进行字符串连接操作  // var a = "a"; a++  => NaN

     6. 如果是非对象的 == 比较,如 true == 1 , null == undefined //会将非数字的转化为数字再比较

         如果其中一个是对象,则会调用toString(); //var s = {}; s == "[object Object]"

     7. 加号运算符更偏爱字符串,比较运算符则更偏爱数字 // "1" + 2 = 12,  "11" <  3 = false

     8. 如果instanceof 右操作数不是函数,则报异常

继续阅读