《ES6标准入门》读书笔记

《ES6标准入门》读书笔记

@(StuRep)javascript

图片发自简书App

let和const命令

  1. ES6新增let命令,用于声明变量,是块级做用域。
  2. let声明的变量不会像var声明的变量发生“变量提高”现象,因此,变量必定要在声明后使用,否则就会报错。
  3. 暂时性死区:只要块级做用域内存在let命令,它所声明的变量就会“绑定”在这个区域,再也不受外部的影响。即在代码块内,使用let命令声明变量以前,这个变量都是不可用的,这在语法上称为“暂时性死区”。
  4. ES6规定暂时性死区和不存在变量提高,主要是为了减小运行时的错误,防止在变量声明前就使用这个变量,致使意外,这样的错误在ES5中很常见。
  5. let不容许在相同做用域内重复声明同一个变量。
  6. const命令用来声明常量,声明了以后就不能再改变,因此在声明的时候就必须赋值,这个命令一样是块级做用域,一样存在暂时性死区。
  7. 对于用const声明的对象,变量名不会指向对象的数据,而是指向对象所在的地址,因此用const声明的复合类型变量中的数据是能够改变的,这点须要小心!

变量的解构赋值

  1. 解构:ES6容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被成为解构。以下:java

    //ES5
    var a = 1;
    var b = 2;
    var c = 3;
    
    //ES6
    var [a, b, c] = [1, 2, 3];
    
    //嵌套
    let [foo, [[bar], baz]] = [1, [[2], 3]];
    foo;//1
    bar;//2
    baz;//3
    
    //若是等号右边不是能够遍历的结构,就没法匹配就会报错。
  2. 解构赋值是容许设置默认值的,在ES6内部使用'==='来判断一个位置是否有值。因此,若是一个数组成员不严格等于undefined,默认值是不会生效的。例如:正则表达式

    [x, y = 'b'] = ['a', undefined];//x='a',y='b'
    [x = 1] = [null];//x=null
  3. 默认值能够引用解构赋值的其余变量,但该变量必须已经声明。例如:编程

    let [x = 1, y = x] = []; //x=1; y=1
    let [x = 1, y = x] = [2]; //x=2; y=2
    let [x = 1, y = x] = [1, 2]; //x=1; y=2
    let [x = y, y = 1] = []; //报错,由于x在使用y做为其默认值的时候y尚未被声明
  4. 对象的解构赋值:json

    var {foo, bar} = {foo: "aaa", bar:"bbb"};
    foo //"aaa"
    bar //"bbb"
对象的解构赋值和数组有一个重要的不一样:数组的元素是按次序排列的,变量的取值由它的位置来决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
  1. 对象的解构赋值能够很方便地将现有对象的方法赋值到某个变量。例如:数组

    let {log, sin, cos} = Math;
    //这样就能够把取对数、正弦、余弦3个方法赋值到对应的变量上面,用起来很方便;
  2. 字符串也能够解构赋值,由于字符串会被转换成一个相似数组的对象。例如:浏览器

    const [a, b, c, d, e] = 'hello';
    a // 'h'
    b // 'e'
    c // 'l'
    d // 'l'
    e // 'o'
    
    let {length : len} = 'hello';
    len //5
  3. 数值和布尔值的解构赋值:解构赋值时,若是等号右边是数值或布尔值,则会先转为对象。
  4. 函数参数也能够解构赋值,例如:babel

    function add([x,y]){
        return x + y;
    }
    
    add([1, 2]) //3
  5. 变量的解构赋值的用途不少,简洁易读:app

    • 交换变量的值[x, y] = [y, x]
    • 从函数返回多个值;
    function example(){
        return [1, 2, 3];
    }
    var [a, b, c] = example();
    • 函数参数的定义;编程语言

      //有序
      function f([x, y, z]){...};
      f([1, 2, 3]);
      
      //无序
      function f({x, y, z}){...};
      f({z:3, y:2, x:1});
    • 提取JSON数据,能够快速提取json对象中的数据;

字符串的扩展

ES6增强了对Unicode的支持,而且扩展了字符串对象。
  1. ES5对字符串对象提供了CharAt方法,返回字符串给定位置的字符。可是该方法不能识别码点大于0xFFFF的字符。因而在ES7中提供了一个at方法,能够识别Unicode编号大于0xFFFF的字符。
  2. includes(),startsWith(),endsWith()方法。JS中只有indexOf方法能够用来肯定一个字符串是否包含在另外一个字符串中,ES6又提供了三种方法:

    • includes():返回布尔值,表示是否找到了参数字符串;
    • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部;
    • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部;
  3. repeat(),repeat方法返回一个新字符串,表示将原字符串重复n次。
  4. padStart(),padEnd():ES7推出了字符串补全长度的功能。若是某个字符串长度未达指定长度,会在头部或尾部补全。padStart用于头部补全,padEnd用于尾部补全。若是原字符串的长度大于或等于指定的最小长度,则返回原字符串。例如:

    'x'.padStart(5, 'ab') //'ababx'
    'x'.padStart(4, 'ab') //'abax'
    
    'x'.padEnd(5, 'ab') //'xabab'
    'x'.padEnd(4, 'ab') //'xaba'
    
    'xxx'.padStart(2, 'ab') //'xxx'

正则的扩展

  1. 在ES5中,RegExp构造函数只能接受字符串做为参数var regex = new RegExp("xyz", "i");。在ES6中容许RegExp构造函数接受正则表达式做为参数,这时会返回一个原有正则表达式的拷贝。
  2. ES6新增了使用大括号表示Unicode字符的表示法,这种表示法在正则表达式中必须加上u修饰符才能识别。例如:

    /\u{61}/.test('a'); //false
    /\u{61}/u.test('a'); //true
  3. ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。ES5的source属性会返回表达式的正文。

数值的扩展

  1. ES6提供了二进制和八进制数值的新写法,分别用前缀0b(或0B)和0o(或0O)来表示。从ES5开始,在严格模式中,八进制数值就再也不容许使用前缀0表示,ES6进一步明确,要使用0o前缀表示。
  2. Number.isFinite(),Number.isNaN():ES6在Number对象上面新提供了这两个方法,分别用于检查Infinite(是否非无穷)和NaN这两个特殊值。
  3. Number.parseInt(),Number.parseFloat():ES6将全局方法parseInt()和parseFloat()移植到了Number对象上。这样是为了逐步减小全局性的方法,使语言逐步模块化。

    //ES5
    parseInt('');
    
    //ES6
    Number.parseInt('');
    
    Number.parseInt === parseInt; //true
  4. Number.isInteger():该方法用来判断一个值是否为整数。
  5. 新增了一个极小的常量Number.EPSILON,当咱们作计算的时候,若是偏差能够小于这个常量,那么就能够认为计算的结果是正确的。
  6. Number.isSafeInteger():JavaScript可以准确表示的整数范围在-2{53}到2{53}之间,超出的就不能精确表示了,该函数用来判断一个数是否落在这个范围以内。
  7. Math对象的扩展,ES6在Math对象上新增了17个与数学相关的方法:

    • Math.trunc():用于去除小数部分,返回整数部分;
    • Math.sign():用于判断一个数究竟是正数、负数仍是0,整数返回1,负数返回-1,0返回0,-0返回-0,其余返回NaN;
    • Math.cbrt():计算一个数的立方根;
    • Math.clz32():返回一个数的32位无符号数有多少个前导0;
    • Math.imul():返回两个数以32位带符号整数形式相乘的结果,返回的也是一个带符号整数,例如:Math.imul(-1, 8); //-8
    • Math.fround():返回一个数的单精度浮点数形式;
    • Math.hypot():返回全部参数平方和的平方根,例如:Math.hypot(3, 4);//5
    • 还有一些和对数运算、三角函数运算、指数运算相关的方法。

数组的扩展

  1. Array.from():将相似数组的对象和可遍历的对象转为真正的数组;
  2. Array.of():将一组数值转换为数组,例如:Array.of(3, 11, 8) //[3,11,8]
  3. fill()方法,使用给定值填充数组,例如:new Array(3).fill(7) //[7,7,7]
  4. 数组实例的entries()、keys()、和values()方法,主要用来遍历数组,keys()是对键名的遍历,values()是对键值的遍历,entries()是对键值对的遍历;

函数的扩展

  1. ES6以前不能直接为函数的参数指定默认值,因此常常有x = x || "XXX"这样的写法,ES6容许为函数的参数设置默认值,就能够这样写function test(x, y = "xxx"){};,这样的设计还有一个好处就是开发人员阅读别人的代码一眼就能看出来在调用这个接口哪些参数是可省的。此外,这种写法还能够和解构赋值结合使用,很是灵活。
  2. 函数的length属性修改,若是函数中的参数有指定默认值,那么length就不会把这个参数计算进去,例如:(function(a=5){}).length;//0
  3. 做用域问题,若是一个参数的默认值是一个变量,那么这个变量所处的做用域与其余变量的做用域规则是同样的,先是当前函数的做用域,而后才是全局做用域;
  4. ES6引入了rest参数(形式为"...变量名"),用于获取函数的多余参数,这样就不须要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中,例如:

    function add(...values){
        let sum = 0;
        for(var val of values){
            sum += val;
        }
        return sum;
    }
    add(2, 5, 3); //10
    //add函数是一个求和函数,利用rest参数能够向该函数传入任意数目的参数。
  5. 扩展运算符,三个点(...),做用是把一个数组转为用逗号隔开的参数序列。例如:console.log(1,...[2,3,4],5);//1 2 3 4 5;
  6. 扩展运算符替代数组的apply方法,扩展运算符能够直接把数组拆开,例如:

    //ES5
    function f(x,y,z){};
    var args = [0,1,2];
    f.apply(null, args);
    
    //ES6
    function f(x,y,z){};
    var args = [0,1,2];
    f(...args);
  7. 扩展运算符提供了数组合并的新方法:

    //ES5
    [1,2].concat(more)
    //ES6
    [1,2, ...more]
  8. 扩展运算符还能够与解构赋值结合;
  9. ES6还写入了函数的name属性,能够返回函数名,虽然这个属性很早就被各个浏览器支持了,可是在ES6才正式写入;
  10. 箭头函数:ES6容许使用"箭头"(=>)定义函数,例如:

    var sum = (num1, num2) => num1 + num2;
    //等价于
    var sum = function(num1, num2){
        return num1 + num2;
    }
  11. 使用箭头函数有几个注意点:

    • 函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。在js中this的指向是能够改变的,可是在箭头函数中this的指向是不变的;
    • 不能够看成构造函数。也就是说,不可使用new命令;
    • 不可使用arguments对象,该对象在函数体内不存在。若是要用,能够用ES6中的rest参数代替;
    • 不可使用yield命令,所以箭头函数不能用做Generator函数;
  12. 函数绑定:在ES6以后的ES7版本中有一个提案是函数绑定运算符(::),双冒号左边是一个对象,右边是一个函数。这个运算符会自动将左边的对象做为this绑定到右边的函数上面,例如:foo::bar(...arguments)等价于bar.apply(foo,arguments;)。感受函数绑定这个设计很是的便捷,不须要在显式的去绑定一下上下文,期待该提案的经过(目前babel已经支持这个写法了);
  13. 尾调用:就是指某个函数的最后一步是调用另外一个函数;
  14. 尾调用优化:尾调用之因此与其余调用不一样,就在于其特殊的调用位置。函数调用的时候会在内存造成一个‘调用记录’,又称为‘调用帧’,保存调用位置和内部变量等信息。若是在函数A内部调用函数B,那么在A的调用帧上方还会造成一个B的调用帧。等到B执行结束再返回给A,B的调用帧才消失。若是B的内部调用了C,那么还会产生一个调用帧,以此类推,全部调用帧会造成一个‘调用栈’。然而尾调用是函数的最后一步操做,因此不须要保留外层函数的调用帧,由于调用位置、内部变量等信息都不会再用到了,直接用内层函数的调用帧取代外层函数的便可;
  15. 尾递归:函数调用自身称为递归,若是尾调用自身就称为尾递归。递归很是耗费内存,由于须要同时保存成千上百个调用帧,很容易stackoverflow。但对于尾递归来讲,只存在一个调用帧,因此永远不会发生“栈溢出”错误。例如:

    //这是一个阶乘函数,计算n的阶乘,最多须要保存n个调用记录,复杂度为O(n)。
    function factorial(n){
        if(n === 1) return 1;
        return n * factorial(n - 1);
    }
    //改写成尾递归,只保用一个调用记录,则复杂度为O(1);
    function factorial(n, totla){
        if(n === 1) return total;
        return factorial(n - 1, n * total);
    }

    因而可知‘尾调用优化’对于递归操做的意义很是重大,因此一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,全部ECMAScript的实现,都必须部署‘尾调用优化’。这就是说,在ES6中,只要使用尾递归,就不会栈溢出,节省内存。

(未完待续......)

相关文章
相关标签/搜索