分类存档: javascript

《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] 

第八章

    1.

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

        return [x, y];

    }

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

        return [x,y];

    }

    

    m1({x:3}) //[3, 0]

    m2({x:3}) //[3, undefined]     //如果有参数,则不取默认参数,然后对{x,y}赋值

    m2() // [0,0]    //如果没有参数,取默认参数

    

    2.函数的length属性 如果指定了默认值后,length 属性将失真

     (function(a){}).length //1

    (function(a=5){}).length //0

    (function(a, b, v = 5){}).length //2

    3. 函数的length属性不包括rest参数

    (function(a) {}).length //1

    (function(…a){}).length //0

    (function(a, …b){}).length //1

    4.  任何类似数组的对象都可以用扩展运算符转为真正的数组(Map,Set,Ceberatir也可以)

    var nodeList = document.querySelectorAll('div');

    var array = […nodeList];

    5. ()=>{console.log(arugments)} 箭头函数函数体内没有arguments对象

    6.箭头函数的管道机制,即前一个函数的输出是后一个函数的输入

    var s = (a)=> b => a * b

    s(2)(3) //6

    7 尾递归可以极大节省内存

       尾递归的实现往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量都改写成函数的参数。

        function factorial(n, total==1){

            if(n===1) return total;

            return tailFactorial(n – 1, n * total);

        }

        factorial(5);

     8 柯里化 的意思是将对参数的函数转换成单参数的形式。

第九章

    1.Object.is   跟全等号===类似

    不同之处有两个

    Object.is(+0, -0); //false

    Object.is(NaN, NaN)  //true

    

    2.克隆对象

    function clone(origin) {

        return Object.assign({}, origin);

    }

     上面的代码将原始对象复制到一个空对象,就得到了原始对象的克隆。

    不过,采用这种克隆,只能克隆原始对象自身的值,不能克隆它的继承值。如果想要保持继承链,如下:

    function clone(origin){

        let originProto = Object.getPrototypeOf(origin);

        return Object.assign(Object.create(originProto), origin);

    }

    3. 属性的遍历

    for…in  //遍历包括自身和继承的可枚举属性

    Object.keys(obj) //只遍历自身的可枚举属性

    Object.getOwnPropertyNames(obj) //遍历自身和继承的属性包括不可枚举的属性

    Object.getOwnProertySymbols(obj) //返回自身所有的Symbol属性 

    Refect.ownKeys(); 

    Refect.enumerate()

    以上的遍历排序按一下规则:

    1.数字优先,按数字小到大排序

    2.其实是字符串,按生成时间排序

    3.最后是Symbol的值,按生成时间排序

    Object.keys({[Symbol():0, b:0, 10:0, 2:0, a:0]})

    //['2', '10', 'b', 'a', Symbol()]

    4.Object.setPrototypeOf 

    可以修改实例的原型链,如 

    let p = {a:1}

    function d(){};

    d.protype = p

    

    let a = new d();

    let c = new d();

    Object.setPrototypeOf(a, {b:1})

    a.b = 1;

    a.a = undefined;

    注意:c.prototype也会受到牵连

    5.如果扩展运算符的参数是null 或者 undefined,则会忽略不会报错

    let emptyObject = {…null, …undefined} // {} ,不会报错 

  

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; 

狂啃犀牛书-笔记

第二章

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 右操作数不是函数,则报异常

     9.&& 操作符,如果两个操作数都是真值,那么返回第二个真值;否则只是有一个假值,返回假值。如果两个都是假值,则返回假值。 // 12 && 1 = 1, 0 && 1 = 0 , null && 0 = null

     10.所有可执行对象进行typeof运算都将返回"function"  // var a = function(){}, typeof a

      11.用户通过var语句声明的变量不能删除,通过function 语句定义的函数和函数参数也不能删除

      12.void 会忽略计算结果并返回undefined // var i=0;void(++1) //undefined; i ==1

第五章

        1.var 声明的变量是无法通过delete删除

            var bcd = 123;delete bcd //false

            bcd = 123;delete bcd //true

        2.var a =1;var a; // a = 1

        3. 每个case匹配操作实际上是 "===" ,而不是"=="

                n = "1";

                switch(n){

                 case 1:

                 alert(1);

                 break;

                 case "1":

                  alert(2);

                 break;

                }

              //2

         4."default:" 表情可以放置在switch语句内任何地方

        5. function tail(o) {             //返回链表最后一个节点对象

            for(; o.next;o = o.next)

            return o;

        }

        6. 死循环的两种写法

          while(trie){};for(;;){}

        7.var o = {x:1,y:2,z:3}

           var a = [], i = 0;

           for(a[i++] in o)  /* empty */  //将对象值复制到数组中的最简短方法

        8.如果for/in循环体删除了还未枚举的属性,那么这个属性将不会在枚举到。如果循环体内定义了对象的新属性,这些熟悉通常也不会被枚举到     

            var o = {x:1,y:2,z:3};

            for(a in o){

             o['q'] = 4;

             delete o.z;

             console.log(a);

            }

            //x,y

        9.有些浏览器是按照浏览器按照数字顺序来枚举数组属性,但一般是按照定义的先后顺序。

第六章:

       1. var o2 = Object.create(null)  //o2没有prototype,不继承任何东西

        2.  如果o继承自一个只读属性x,那么赋值操作是不允许的。

    var o = {};
    Object.defineProperties(o, {
        y: {value:2, writable: false}
    })
    
    var o2 = Object.creat(o);
    o2.y = 1; //o2.y = 2

        3.Object.keys() //返回对象自有可枚举的属性,prototype上的不枚举

           Object.getOwnPropertyNames //返回对象所有属性,包括不可枚举的,get/set不返回,prototype上的不枚举

        4.var o = {

             data:123,

             get a(){/*do something*/} ,      

             set a(val){/*do something*/} ,

            }

            这里没有使用function 而是直接使用set/get 写法上要注意

        5. 这个函数可以自动返回传入的所有数据类型

        function classof(o) {        
            if(o === null) return ""Null";
            if(o === undefined) return "Undefined";
            return Object.prototype.toString.call(o).slice(8,-1);
        }
        classof(null) //Null
        classof(1) //Number
        classof(false) //Boolean
        classof({}) // Object
        classof(function a(){}) //Function

        6.JSON.stringify() //只能序列化对象可枚举的自有属性

第七章

         1.数组最大的索引为 2^32-2
         2.[,,]只有两个元素而非三个

         3.Arry可以像对象一样赋值,名字非负整数的话,它就当做常规的对象属性。

         4.稀疏数组就是不连续索引的数组

         5.如果数组是稀疏的,length属性值大于元素的个数

         6.修改数组的length值时,大于或等于length个数的元素将被同时删除

         7.如果让一个数组元素不可配置,那就不能删除它。如果不能删除它,length属性不能设置为小于不可配置元素的索引值。

         8.对一个数组元素使用delete不会修改数组的length属性. 同时变成稀疏数组 //var a = [1,2,3]; delete a[2];  length 依然是 3

         9.for/in 循环能够枚举继承的属性名,不完全适用于数组循环

            Array.prototype.abc =123;
            s = [1,2,3];
            s.a = 123;
            for(var z in s){console.log(z)}  //0,1,2,a,abc

         10.forEach() 只遍历正整数索引

          11.forEach,map,filter,every,some…等方法,如果是稀疏数组,对于不存在的元素步调用传递的函数。

          12.forEach,map,filter,every,some…第一个参数是一个函数,第二个参数是可选的,如果有将作为调用函数的this

          13.一旦every()和some() 确认该返回什么值,他们就会停止遍历数组元素。   every如果碰到return为false,则停止,some如果碰到return为true,则停止。

          14.reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生产单个值。

           15.

                var isArray = Function.isArray || function(o) {

                        return typeof o === "object" && Object.prototype.toString.call(0) === "[object Array]"

                }

                实际就是ES5中 Array.isArray()函数所做的事情。

            16.利用数组的特殊方法,可以方便的对字符串做些事情。

                s = "JavaScript";

                Array.prototype.join.call(s, " ")  // J a v a S c r i c p

                Array.prototype.filter.call(s,

                    function(x) {

                        return x.match(/[^aeiou]/);   //不匹配这些字母

                    }

                ).join("")                                        //  JvScrpt

               17.push(),sort(),reverse(),splice() 等数组方法会修改数组,它们在字符串上是无效的,因为字符串是基本类型不可改变。
第八章

    1.函数的声明不能出现在循环,条件判断,try/cache/finally,with语句中

    2.var strict = (function(){ return !this;  })();  //用来确定当前较薄是否严格模式

    3.如果构造函数显式地使用return语句,返回一个对象,那么返回的就会是这个对象

        function a(){
        this.c = 1;
        return {a:1};
        }
        var b = new a();    //{a:1}

        如果return语句美元指定返回值,或者返回一个原始值(Undefined、Null、Boolean、Number 和 String),那么就会忽略返回值,同时使用这个新对象作为调用结果。

        function a(){
        return 4;
        }
        var b = new a();   //{}

     4.在非严格模式下,形参和实参上相同变量的不同命名,所以改argument 会影响参数

        function f(x) {
            console.log(x);
            arguments[0] = null;
            console.log(x);        //null
        }
        f(1);

        在严格模式下,函数无法使用arguments作为形参名或局部变量名,也不能给arguments赋值。

    5.函数的length属性是只读属性,它代表函数实参的数量。

    6.严格模式下,call()和apply()的第一个参数都会变成this的值,无论是否原始值,非严格模式下,null和undefined会被window替换,其他原始值会被相应的包装对象替代。

    7.bind除了传入第一个this,还传入第二第三个参数,如

        var sum = function(x,y) { return x + y};

        var succ = sum.bind(null, 1);

        succ(2);

        //这样叫做函数"柯里化"

    8.如果bind()返回的函数用作构造函数,将忽略传入bind()的this

    function a(){    
        this.c = 1;
        console.log(this);
    }
    var b = a.bind({a:1});
    var c = new b();        //a {c: 1}

        由bind()方法返回的函数不包含prototype属性,并且由这些绑定的函数用作构造函数时所创建的对象从原始的为绑定构造函数中继承prototype

    function a(){    
        this.c = 1;
        console.log(this);
    }
    a.prototype.abc = 1;
    var b = a.bind({a:1});
    var c = new b();    //{c:1}  prototype = {abc:1};

9.Function()构造函数可以动态的创建函数。

10.由Function创建的代码编译会在全局作用域执行。

    var scope = "global";    
    function constructFunction() {
        var scope = "local";
        return new Function("return scope");
    }
    constructFunction()();   // =>"global"

11. isFunction() //

    function isFunction(x){    
        return Object.prototype.toString.call(x) === "[object Function]";
    }

第九章

    1.instanceof 实际上是检测对象的继承关系,而不是检测创建对象的构造函数

    2.鸭式辨型是用来监测一个对象,是否具有某个对象的方法,

    比如 对象People 有 say,eat 方法, 对象Man,Woman 可能有say或者eat方法   Poeple.ensureImplements(['Woman','Man']);    

    目的是检测Woman和Man是否实现了叫say 和 eat 的方法,实现了返回true,没实现报错。

    通常是写框架的时候检测输入是否合法。

    3.以前认为Object对象不能作为对象的Key,可以换种思维,给Object变唯一的id

var v2s = function (val) {
	switch(val) {
		case undefined: return 'u';
		case null: return 'n';
		case true: return 't';
		case false: return 'f';
		default: switch(typeof val) {
			case 'number': return '#' + val;
			case 'string': return '"' + val;
				default: return '@' + objectId(val);
		}

		function objectId(o) {
			var prop = "|**objectid**|";
			if(!o.hasOwnProperty(prop))
				o[prop] = Set._v2s.next++;
			return o[prop]
		}
	}
}
v2s.next = 100;

     

    4.   defineProperty单独设置value的话,其余enumerable,configurable都是false

    var p = {};    
    Object.defineProperty(p,"a",{value:2});
    Object.defineProperties(p, {b:{value:1}})

    5.自己在prototype增加的属性可被枚举,可写,可配,但getOwnPropertyDescriptor返回undefiend;

    6.Object.create() 的第二个参数,用于属性描述类似defineProperties()的第二个参数

    7.( 符号开始的语句是函数表达式,而不是函数定义。

第10章

    1.圆括号在正则表达式中有3种作用:

        1.子表达式

        2.定义子模式,就是给js match 分组的

        3.表达式中的位置 \1

    2. \b匹配单词边界   \B匹配非单词边界

    3.(?=p)(?!p) 锚字符匹配

    4. search 不支持全无检索,会默认忽略修饰符g

    5."javascript".search("java")  //如果search参数不是正则表达式 ,则首先会通过RegExp构造函数将它转换成正则表达式

    6.split()可以用正则表达式作为参数作为切割

    7.写了一个可以在一个字符串中匹配key value的fun

function getAttr(str){
    var reg = /(\d)+(?=[\s\/::]|\D)[\s\/::、]?([^0-9\s,;)]+)/,arr = [],index = 0;
    while(index<str.length){
        var result = str.substr(index).match(reg);
        if(!result)break;
        arr.push({key:result[1], value:result[2]});
        index = str.indexOf(result[0]) + result[0].length;
    }
    return arr;
}

getAttr("状态(0:全部 1:待退卡 2:已退卡)");  //[{key:"0",value:"全部"},{key:"1",value:"待退卡"},{key:"2",value:"已退卡"}]
getAttr("状态(0全部1待退卡2已退卡)"); //[{key:"0",value:"全部"},{key:"1",value:"待退卡"},{key:"2",value:"已退卡"}]
getAttr("1:正常;2:异常)");//[{key:"1",value:"正常"},{key:"2",value:"异常"}]
getAttr("删除状态 0:未删除 1:已删除");//[{key:"0",value:"未删除"},{key:"1",value:"已删除"}]

第13章

  1. <script>元素包含其值不被浏览器识别的type属性时,它会解析这个元素但不会尝试显示或执行它的内容。

   2.为什么我们经常需要在href后面加javascript:void(0)

    比如 <a href="javascript:new Date().toLocalTimeString();">

                time

            </a>

    部分浏览器点击这个标签之后,会使用返回的字符串作为待显示新文档的内容。

    因为 void() 总是返回undefined 

   3. 在浏览器async,defer都支持,并同时存在的情况下,async优先,忽略defer

   4. Doctype跟页面渲染模式有关

   5. <iframe>元素定义一个sandbox属性,会禁用脚本

第14章

    1.loaction.replace() 不会加入历史栈

    2.location.hash="top"  和 location="#top"    如果页面没有id为 top的元素,浏览器会跳到文档顶部

    3.如果一个窗口中包含多个子窗口 如 <iframe> ,子窗口的浏览器历史会按时间顺序穿插在主窗口的历史中。

    4.通过window.xx 可以饮用到 id="xx"的元素, 前提是 xx没被使用,delete window.xx 不能删掉元素

第15章

    1.name属性直邮在少数元素中有效,包括 表单,表单元素,<iframe> ,<img>

    2.getElementsByTagName("*") //获得文档中所有元素

    3.document.documentElement  //指html根元素

    4.getElementsByClassName()  可以由多个空格隔开的标识符组成,且顺序无所谓

    5.   firstChild,lastChild,nextSibling,previoursSibling是节点,

          firstElementChild,lastElementChild,nextElementSibling.preiousElementSibling 是元素

    6.nodeType = 8 Comment节点 代表注释

        nodeType = 11 DocumentFragment节点 代表虚拟节点

    7.cloneNode() //传true 复制子节点 传false只复制自己不含子节点

    8.elementFromPoint(x,y)   //根据x,y获取对应的节点

    9.了解DocumentFragment 给我们带来的性能优化

        DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。 不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作

// Create the fragment
var fragment = document.createDocumentFragment();
//add DOM to fragment 
for(var i = 0; i < 10; i++) {
    var spanNode = document.createElement("span");
    spanNode.innerHTML = "number:" + i;
    fragment.appendChild(spanNode);
}
//add this DOM to body
document.body.appendChild(spanNode);

第16章

    1.box-sizing:border-box; // width,height 属性讲包含边框和内边距。当想以百分比形式为元素设置总体尺寸,📮一像素单位制定边框和内边距时,边框盒模型特别有用:

    <div style="box-sizing:border-box;width:50%;padding:10px; border:solid black 2px;

     2.window.getComputedStyle(element,':after') 可以获取伪元素的css样式

    3.CSSStyleDeclaration对象能获取确切的绝对值单位 比如 %=>px ,rem=>px ,red => (255,0,0)

第17章

    1.mousedown->mouseup->click

    2.mouseover,mouseout会冒泡

       mouseenter,mouseleave不会冒泡

    3.keydown->keypress->keyup 单点

        keydown->keypress->  keydown->keypress->  keydown->keypress->  keydown->keypress->  keydown->keypress->。。。->keyup 长按

    4.focus,blur冒泡

        focusin, focusout不冒泡

    5. Safair 有一个专有的手势事件 gesturestart.gestureend 可以快速实现缩放拖拉旋转

    6.element.onXXXX = fun   //定义多个会被覆盖

        element.addEventLinstner('xxx',fun) ;fun触发顺序是按注册顺序决定

    7.addEventListener使用相同的参数多次调用,只会调用第一次

    8.attachEvent("onXXXX", fun)//  attachEvent的第一个参数要 "on" 开头

    9.attachEvent的注册次数和调用次数一样

    10.ie8及之前版本的even是全局对象

    function handler(event){

        event = event || window.event;

    }

   11.attacheEvent 回调的fun 的this是window对象

   12. 1. html属性注册的程序一直优先调用

         2.使用addEventListener()注册的处理程序按照它们的注册顺序调用。

    13.文档上的书籍大部分都会冒泡,除了focus,blur,scroll

    14.可以通过取消textinput,textInput,keypress的默认事件达到阻止字符输入的目的

第18章

    1.请求头的method方法除了常见的POST,GET还有

        DELETE : 删除数据

        HEAD:只要求返还请求头相关信息

        PUT:更新数据

        OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法

    2.XMLHttpRequset 也支持同步响应,把false作为第3个参数传递给open()   //request.open("GET", url, false)

    3.XMLHttpRequset可以上传文件,必须设置“multipart/form-data”表单编码。使用了  new FormData() ,相当于设置了 “multipart/form-data”

    4.关于ajax跨域请求的问题:

        ajax跨域发请求的时候,请求是有到服务器了,服务器如果head里面包含CORS并通过的话,想普通ajax一样使用。如果head里面没有包含CORS的话,服务器放回数据到浏览器,浏览器检查是否有CORS,如果没有,则抛出错误。

        关于COOKIES,如果是同域,则会自动带上COOKIES,否则,不带,

    5. node.scrollIntoView()    //把node元素放置到视图左上角

    6. xhr.withCredentials = true; //支持跨域发送cookies,withCredentials兼容性不好,通常用来检查浏览器是否支持CORS

    7.localStorage 和 sessionStorage 可以储存结构化数据(对象和数组),也可以存储原始类型数据,还可以存日期,正则等对象,但是目前浏览器都只实现字符串类型存储。

    8. cookie 有一个secure 如果为true,则它只能在https才能传递

    9.RFC 2965 规定:每个域名分配不超过20个cookies,每个cookies不能超过4kb

第22章

    1.结构性复制的意思是

        能复制Date对象,RegExp对象,ImageData对象,FileList对象,File对象,Blob对象,但是不包括函数,错误对象,窗口对象,文档,元素等宿主对象的方法
    2.Blob:通常用来表示本地文件,URL以及数据库以外的大数据块

        和data://URL不同,data://URL会对内容进行编码,Blob URL只是对浏览器存储在内存或者磁盘上的Blob的一个引用

    3.通过Blob,可以直接不上传服务器,就能显示本地图片在浏览器,或者读取本地文本在浏览器,不需要服务器辅助。

    4.通过Blob,还可以忽略跨域问题,像File:URL会有跨域问题。Blob就没有,可以顺利的在canvas上使用toDataURL() 

《JavaScript高级程序设计》第二次阅读笔记

第二章

  1. <!CDATA[   javascriptCode  ]]>   在XHTML中javascript可以用这个标记保证内嵌的javascript代码能正常运行,因为XHTML标准比较严格,很多标签都不能被解析。

  2. defer和async标记都不能保证是在DOMContentLoading之前或之后执行,区别就只是保证顺序执行而已。

第三章

  1. 严格模式下,使用未定义的变量会导致错误。

  2. nul值表示一个空对象的指针,而这也是使用typeof操作符检测null值时返回“object”的原因

  3. 无论在什么情况下都没必要把一个变量的值显式地设置为undefined,但是对于null,只要在保存[对象]的变了还没有真正保存对象,就应该明确地然它保存为null值。

  4. undefined 和 null,没有任何属性和方法

   5.要把都一个值转换为字符串,可以用加号把它与一个字符串“xxx”加在一起

   6.并非所有浏览器的BOM与DOM都属于Object

   7.+操作符 会像Number()一样对这个值执行转换。-号同理,不过变成负数。

   8.do-while 循环的代码至少会执行一次。

   9.for(; ;){ doSomething(); } 无限循环

   10.通过for-in循环输出的属性名的顺序是不可预测的,具体先后顺序因浏览器而异。

   11.swicth{case xxx:} 在比较时是使用全等操作符 ===,所以10不等于“10”

   12. 标准模式下arguments与参数会相互同步,严格模式下不会同步。

标准模式下:
function add(a){
arguments[0] = 10;
console.log(a); //10
}
add(1);
严格模式下
function add(a){
        "use strict";
arguments[0] = 10;
console.log(a);//1
}
add(1);

第4章

  1. 给基本类型添加引用会被浏览器直接忽略,也不会报错。

   2.要检测一个变量是不是基本数据类型用typeof,typeof null 等于object。如果用instanceof操作符检测基本类型的值,则翻回false.

第5章

   1.对象的key只能用字符串作key

   2.数组的toString()方法会返回由数组中每一个值的字符串形式拼接逗号分隔而成的字符串。所以如果[{a:1},2] toString() 等于 [object Object],2

   3.concat()方法可以添加字符串,也可以添加数组。如:

    var colors = ["red","green","blue"];

    var colors2 = colors.concat("yellow",["black","brown"]);

    alert(colors);//red,green,blue

    alert(color2);//red,green,blue,yellow,black,brown

   4.Array的5个迭代方法,都是给定运行参数function(value,index,array){}

    1.every() //返回布尔值,每一项都是true的话,返回true

    2.filter() //将返回true的项组成新的素组

    3.forEach()//仅仅循环数组一次.没有返回值

    4.map()//将每一项的返回值组成新的数组

    5.some()//返回布尔值,其中一项返回true,则返回true

   5.关于函数是否能复制,目前只能想到eval 这种方法了 eval("var z = "+s1.toString());

   6.创建函数有一种新的思路

      var sum = function(num1, num2){

              return num1 + num2;

      }

    等于

   var sum = new Function("num1","num2","return num1 + num2");

    7.严格模式下,访问arguments.callee会导致错误。

    8.函数的length等于参数的数量,如function a(a,b){}; a.length //2

    9.在严格模式下,作为方法调用,则this等于undefined,在非严格模式,等于window.

    10.用Boolean,Number,String创建的变量,与直接定义同名的基本类型不一样。

    var value = "25";

    var number = Number(value); //转换函数

    alert(typeof number);  //number

    var obj =new Number(value); //构造函数

    alert(typeof obj); //object

    主要表现在number就是number类型的基本类型,但是obj是一个number对象,它各种属性都跟基本类型一样。

  10. slice(开始index,结束index)

         substr(开始index,length)  

         substring(开始index,结束index)  开始和结束都不接受负数。

   11.所有全局作用域中定义的属性和函数,都是Global对象的属性。如isNaN(),isFinite(),parseInt(),parseFloat();

第6章

    1.只要创建一个新函数,就会为该函数创建一个prototype,并自动获得constructor属性指向函数本身。

    2.alert(Person.prototype.isPrototypeOf(person1));//true

      isPrototypeOf可以检测person1的prototype属性是否属于Person

   3.person1.hasOwnProperty("name") //hasOwnProperty方法可以确定属性存在方法中还是存在实力中。

   4."name " in person1 //单独使用in操作符会在通过对象能过访问属性时返回true。

   5.通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。

第7章

    1.

    var name = "the window";

    var object = {

        name : "My Object",

        getName: function(){

            return this.name;

        }

    }

    object.getName();    //"My Object"

    (object.getName)(); //"My Object"    object.getName == (object.getName)

    (object.getName = object.getName)();//"the window"  相当与x=object.getName;x();

第8章

    1.iframe框架之间使用instanceof不适用

第9章

    1.每次修改location的属性(hash除外),页面都会以新的url重新加载;location="xxx" 等于loaction.pathname="xxx"

第10章

    1.如果传入到appendChild()中的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。

    2.如果参照节点是null,则insertBefore()与appendChild()执行相同的操作。

    3.cloneNode()//在参数为true的情况下,为深度复制,复制节点和整个子树;在参数为false的情况下,为浅复制,只复制节点自己。

       cloneNode 不会复制绑定事件。

    4.document.write();在页面呈现的过程中运行,则之间向页面追加内容。如果在加载结束后调用document.write();则重写整个页面。

第11章

    1.footer.dataset.xx="asd";等于<footer data-xx="asd"></footer>  //可以取代setAttribute('data-asd',"xx");的方法。

     2.一个更强大的DOM操作方法:insertAdjacentHTML(); 有4种插入模式:

        beforebegin:     //作为前一个同辈元素插入

            element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>")

        afterbegin:        //作为子元素在内容前插入

            element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>")

        beforeend:        //作为子元素在内容最后插入

            element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>")

        afterend:        //作为后一个同辈元素插入

            element.insertAdjacentHTML("afterend", "<p>Hello world!</p>")

第13章

    1.ie8及以前的版本只支持attachEvent,只在冒泡阶段触发。

    2.ie和其它浏览器触发顺序有点相反

        footer.addEventListener('click',function(){alert(1);})

        footer.addEventListener('click',function(){alert(2);})

        先显示1,再显示2

        footer.attachEvent('click',function(){alert(1);})

        footer.attachEvent('click',function(){alert(2);})

        先显示2,再显示1

    3.event.currentTarget //仅代表被绑定的那个元素。当用事件代理时,currentTarget!=target

    4.只有cancelable属性设置为true的属性,才可以使用preventDefault()来取消默认行为。

    5.只有在设置了<script>元素的src属性并添加到文档后,才下载javascript文件

    6.在unload事件中访问dom,不一定能访问。看浏览器而异。

    7.浏览器最小化也会触发resize事件

    8.除了mouseenter和mouseleave,所有鼠标事件都会冒泡。

    9.针对mouseover和mouseout事件,event对象的relatedTarget提供了相关元素的信息。

第14章

    1.tabIndex:表示当前字段的切换(tab)序号 

    2.对于<input>和<textarea>元素,当失去焦点且value值被改变时,才会触发chang事件

       对于select,只要选择了不同的选项就会马上触发。

    3.表单的selectionStart/selectionEnd属性       //可以知道选择字符串的位置

    4.document.designMode = "on";可以使文档的text处于可编辑状态

    5.<div contenteditable >1</div>  //使某个元素处于可编辑状态

    6.document.execCommand("bold",false,null); //用这个命令可以实现大部分的编辑器功能。

    7.document.getSelection();  与选中文本相关的api

第20章

    1.JSON.stringify(obj, [key1,key2]|function(key,value){return xxx},int缩进空格数);

        JSON.stringify有第二个参数,这个参数可以是数组,也可以是函数。

        如果是数组,则返回数组中只返回数组中列出的key;

        如果是函数,例子:

var json = {"a":1,"b":2,"c":3}

var sx = JSON.stringify(json,function(k,v){
	switch(k){
		case "a":
			return 4
		case "b":
			return 5;
		case "c":
			return 6;  //如果是undfined或是没返回则最终数组是{"a":4,"b":5}
		default:
			return v;
	}
})
console.log(sx); //{"a":4,"b":5,"c":6}

这里要注意,default不仅是为了返回abc以外的key,函数第一次运行,返回的k是空字符串,v是json。若这时候改变v,则后面的k将按照改变后的v进行操作。

    2.toJSON方法

    var json = {"a":1,"b":2,"c":3,d:{"c":4,toJSON:function(){ return this.c}}}

   JSON.stringify(json);// "{"a":1,"b":2,"c":3,"d":4}"

    如果在只要有一个对象,不管是父对象还是子对象,有名叫toJSON的方法的时候,真个对象可以用这个方法作为过滤。

asdd.png

巧用JSON的第二个参数,可以做到很多事情。

   3.JSON.parse也有第二个参数作为函数的方法

JSON.parse('{"a":1}',function(key,value){

console.log(value);

});

    这里会触发2次,第一次是1,第二次是对应的空对象或者空数组。

第22章

    1. function isArray(value){

            retrun Object.prototype.toString.call(value)  == "[object Array]"

    }

     function isFunction(value){

            retrun Object.prototype.toString.call(value)  == "[object Function]"

    }

    用于检查是否原生数组或者方法。

    2.防篡改对象:

        1.Object.preventExtensions();    //不能增加,但可以修改删除

        2.Object.seal()   //不能增删,但可以改。

        3.Object.freeze() //完全密封,既不能改,也不能加属性

    3.setInterval();

        1.某些间隔会被跳过

        2.多个定时器的代码之间的间隔可能会比预期小。

第24章

    1.尽可能做好初始化类型:

        var found = false;    //布尔型

        var count = -1;    //数字

        var name = ""; //字符串

        var person = null; //对象

     2.关于事件回调处理的最佳事件

         1.勿将event对象传给其它方法;只传来自event对象中所需的数据

         2.任何可以在应用层面的动作都应该可以在不执行任何事件处理程序的情况下进行

         3.任何事件处理程序都应该处理事件,然后将处理转交给应用逻辑。

     3.Page Visibility API 可以知道页面处于最小化,还是标签页中,还有触发事件。

对象属性的理解

var person = {name:"jie",age:18};
如果只是这样定义一个object,那么configurable:true,Enumerable:true,writable:true;

要改变这些属性类型,需要显式的调用Object.defineProperty
Object.defineProperty(person,"name",{
    configurable:false, //能否被删除,能否修改访问属性,默认true,如果在false重设defineProperty会报错
    Enumerable:false,//能否被for-in,默认为true
    writable:false, //能否修改,默认为true
    value:"gax"
})

console.log(person); //{name: "gax",age:18}
person.name = "abc";
console.log(person); //{name: "gax",age:18}

[configurable]
Object.defineProperty(person,"name",{
    configurable:false,
	value:"jie"
})
delete person.name; //报错
Object.defineProperty(person,"name",{
    value:"jie2"
})//报错
configurable如果被设置false,那么如果delete会报错,如果configurable,Enumerable,writable,value中,任意一项于初始值不一致,都会报错

[Enumerable]
var person = {name:"jie"};
Object.defineProperty(person,"age",{
    Enumerable:false,
	value:"18"
})

for(var x in person){
	console.log(x);
}
//只有name

[writable]
var person = {};
Object.defineProperty(person,"age",{
    writable:false,
    value:"18"
})
person.age = 21;//依然是 18

如果
var person = {};
Object.defineProperty(person,"age",{
    value:"18"
})
那么Object.getOwnPropertyDescriptor(person,"age")//{value: 18, writable: false, enumerable: false, configurable: false}
全部未定义的默认全部false

同理适用于new出来的实例:
function person(){
	Object.defineProperty(this,"age",{
		writable:false,
		value:"18"
	})
}
var son = new person();
这样就能做到对象中的权限控制。

当变量提升遇到作用块

function aaa(){
    a();
    if(true){
        function a(){
            console.log(1);
        }
    }
    if(false){
        function b(){
            console.log(2)
        }
    }
    function a(){
        console.log(3);
    }
}
与
function aaa(){
    a();
    function a(){
        console.log(3);
    }
    if(true){
        function a(){
            console.log(1);
        }
    }
    if(false){
        function b(){
            console.log(2)
        }
    }
}

结果都是3,证明fuction的变量提升是只在当前作用块内。

关于JS页面阻塞的实验

问题1:如果遇到JS阻塞,那么它的阻塞是指阻塞DOM建立,还是阻塞显示(渲染),如果是阻塞DOM建立,那么是全部DOM都阻塞还是部分阻塞?

[DEMO]

现象:先出现红色,3秒后出现蓝色。

结论:说明JS只是阻塞JS脚本下面的DOM。

问题2:如果这个JS阻塞不是因为外链下载消耗的阻塞,而是<script>内部js的计算运行造成的阻塞,那么<script>上面的DOM会被渲染吗?

[DEMO]

现象:红色蓝色一起出现。

结论:说明JS脚本的阻塞如果是[网络原因],就会部分阻塞,如果是[运行原因],就会全部阻塞。

关于 Android Webview SDK 的研究

关于Angular 2.0 的探索

2015年,由于React Native发布异常火爆,引出了她背后的框架ReactJS,ReactJS,在前端届也因此变得非常火爆。

目前很多人拿React和Angular2作比较。我资历尚浅,不作过多评论。今天在这里写一下这几天对Angular的研究一些总结。

Angular 2.0 分为 TypeScript,JavaScript,和Dart版本,目前都是beta版。 继续阅读 »