美文网首页
(持续更新...)ES6学习笔记(一)

(持续更新...)ES6学习笔记(一)

作者: 苦苦修行 | 来源:发表于2018-07-29 17:18 被阅读0次

    参考文献:ECMAScript 6 入门 — 阮一峰
    引申阅读:Unicode编码方案概述

    提醒自己:ES6中不但新增了许多新语法新概念,重要的是还新增了很多API,所以平时在开发中要多查多用!
    

    2015年后的JS标准都可称为ES6


    ES5中实行的是 提升 的原则,ES6中不允许 提升,所以在声明之前访问变量都是报错的(let、const等)

    变量提升:

    var tmp = new Date();
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }
    f(); // undefined
    

    这段代码本质上是这样的:

    var tmp = new Date();
    function f() {
      var tmp = undefined;
      console.log(tmp);
      if (false) {
        tmp = 'hello world';
      }
    }
    f(); // undefined
    

    函数提升:

    function f() { console.log('I am outside!'); }
    (function () {
      if (false) {
        // 重复声明一次函数f
        function f() { console.log('I am inside!'); }
      }
      f();
    }());
    
    

    这段代码运行在ES5环境中,本质上是这样的:

    // ES5 环境
    function f() { console.log('I am outside!'); }
    (function () {
      function f() { console.log('I am inside!'); } // 把整个函数声明给提升了
      if (false) {
      }
      f(); // 'I am inside!'
    }());
    
    // ES6 环境
    function f() { console.log('I am outside!'); }
    (function () {
      var f = undefined; // 是这样提升的,好像更符合我们的理解吧
      if (false) {
        function f() { console.log('I am inside!'); }
      }
      f();
    }());
    // Uncaught TypeError: f is not a function
    

    • 顶层对象:

    var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

    var a = 1;
    // 如果在 Node 的 REPL 环境,可以写成 global.a
    // 或者采用通用方法,写成 this.a
    window.a // 1
    
    let b = 1;
    window.b // undefined
    

    ② 在浏览器中顶层对象是window对象,在Node中是global对象。在不同的环境中,顶层对象是不一样的,可不可以统一下?

    垫片库system.global模拟了这个提案,可以在所有环境拿到global

    // CommonJS 的写法
    require('system.global/shim')();
    // ES6 模块的写法
    import shim from 'system.global/shim'; shim();
    

    上面代码可以保证各种环境里面,global对象都是存在的。

    // CommonJS 的写法
    var global = require('system.global')();
    
    // ES6 模块的写法
    import getGlobal from 'system.global';
    const global = getGlobal();
    

    上面代码将顶层对象放入变量global


    解构

    可遍历的对象才可以解构,比如Object对象(内部属性可遍历)、数组等。

    所谓的解构,就是按照对象的结构,将各个变量组合起来,ES6会根据对应关系,一一给这些变量赋值。数组是按照先后顺序依次解构,对象是根据属性名进行解构

    对象解构的本质let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
    也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。所以,你可以简写成let { foo, bar } = { foo: "aaa", bar: "bbb" };你如果想用其他的变量名,可以这样let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" };f的值就是aaab的值就是bbb

    // 错误的写法
    let x;
    {x} = {x: 1};
    // SyntaxError: syntax error
    

    上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

    // 正确的写法
    let x;
    ({x} = {x: 1});
    
    知识点引申:最外层如果是{},代表一个代码块;最外层如果是(),代表一个表达式,或语句。
    • 字符串解构

    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    
    let {length : len} = 'hello'; //字符串对象有 length 属性
    len // 5
    
    • 数值和布尔值的解构赋值

    let {toString: s} = 123;
    s === Number.prototype.toString // true
    
    let {toString: s} = true;
    s === Boolean.prototype.toString // true
    
    解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
    • 函数参数解构赋值

    需要特别注意以下这种情况,易出错:

    function move({x = 0, y = 0} = {}) {
      return [x, y];
    }
    
    move({x: 3, y: 8}); // [3, 8]
    move({x: 3}); // [3, 0]
    move({}); // [0, 0]
    move(); // [0, 0]
    

    上面定义move函数时,包含了两层意思:如果调用move函数时传入参数,则参数按照{x = 0, y = 0}解构;如果调用move函数时未传入参数,则将{}作为默认参数

    变化一下:(换汤不换药)

    function move({x, y} = { x: 0, y: 0 }) {
      return [x, y];
    }
    
    move({x: 3, y: 8}); // [3, 8]
    move({x: 3}); // [3, undefined]
    move({}); // [undefined, undefined]
    move(); // [0, 0]
    

    上面定义move函数时,包含了两层意思:如果调用move函数时传入参数,则参数按照{x, y}解构;如果调用move函数时未传入参数,则将{ x: 0, y: 0 }作为默认参数

    • 圆括号的使用

    可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。

    啥是赋值语句,啥是声明语句?
    答:let a = 1;就是声明语句,a = 1;就是赋值语句;函数声明中的参数声明也是声明语句

    啥是模式,啥是非模式?
    答:像{ o: { p: n } } = { o: { p: 2 } }; 中,op就是模式, n就是非模式,也就是说,匹配对象中属性名的就是模式部分,和值相关的(本质上就是你声明的变量名)就是非模式部分。

    字符串扩展

    ES6中对ES5中不能正确处理码点大于0xFFFF的字符进行了优化

    没搞明白为啥"\uD842\uDFB7"==="\u{20BB7}"true,而且他们都代表汉字 "𠮷"(这个汉字由两个字符组成),他们的二进制表示明明不同啊!有知道原因的吗?(后续:终于搞明白了,参考文献:Unicode编码方案概述,简单来讲就是在Unicode编码中,为全世界所有的字符都指定了唯一的编号,但是前655362^16\uFFFF个编号对应了世界上最常用的字符,同时在这前65536个编号的0xD800-0xDFFF(十进制55296~57343)这个区间是特殊的,我们可以用这个区间的两个编号表示编号大于65536的字符。很明显,"\uD842""\uDFB7"都是处于这个区间的,所以就可以表示大于65536的编号为"\u{20BB7}"的汉字"𠮷"了)

    因为有像 "𠮷" 这种文字的存在,所以遍历字符串的最好实现应该用for...of循环,用for (let i = 0; i < text.length; i++)这种循环碰到 "𠮷" 这种情况会有问题

    标签模板

    alert`123` // 这就是标签模板
    // 等同于
    alert(123)
    

    标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。

    如果模板字符里面有变量,会先将模板字符串先处理成多个参数,再调用函数。

    let a = 5;
    let b = 10;
    
    tag`Hello ${ a + b } world ${ a * b }`;
    // 等同于
    tag(['Hello ', ' world ', ''], 15, 50);
    

    研究上面代码会发现,如果有n个变量(像${}的这种)会将字符串模板分割成n+1

    “标签模板”的一个重要应用,就是过滤 HTML 字符串,防止用户输入恶意内容。

    let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
    
    function SaferHTML(templateData) {
      let s = templateData[0];
      for (let i = 1; i < arguments.length; i++) {
        let arg = String(arguments[i]);
    
        // Escape special characters in the substitution.
        s += arg.replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;");
    
        // Don't escape special characters in the template.
        s += templateData[i];
      }
      return s;
    }
    

    标签模板的另一个应用,就是多语言转换(国际化处理)

    console.log`123`
    // ["123", raw: Array[1]]
    

    上面代码中,console.log接受的参数,实际上是一个数组。该数组有一个raw属性,保存的是转义后的原字符串。

    进一步用代码说明:

    tag`First line\nSecond line`
    
    function tag(strings) {
      console.log(strings.raw[0]);
      // strings.raw[0] 为 "First line\\nSecond line"
      // 打印输出 "First line\nSecond line"
    }
    

    raw属性的这部分功能,String.raw()也能实现,例子:

    String.raw`Hi\n${2+3}!`;
    // 返回 "Hi\\n5!"
    
    String.raw`Hi\u000A!`;
    // 返回 "Hi\\u000A!"
    

    ES6会对模板字符串中的特殊符号进行转义,比如会转义\u{20bb7},但如果是遇到\unicode这种转义失败的怎么办呢?

    • 在ES6的最新标准中,对标签模板中的字符串如果转义失败,会返回undefined不报错
    • 对模板字符串还是会报错

    例如:
    let bad = `bad escape sequence: \unicode`; // 报错
    tag `bad escape sequence: \unicode`; // 不报错


    正则扩展

    /foo[^]bar/.test('foo\nbar')
    // true
    

    why? 正则表达式中^的两种意思^可以表示从开头匹配;表示排除,也就是说”[]”代表的是一个字符集,^只有在字符集中才是反向字符集的意思。

    这一部分好烦,不想看了!
    

    数值扩展

    浮点计算是不准确的,比如0.1 + 0.2 === 0.3 // false
    怎么解决?ES6Number对象上面,新增一个极小的常量Number.EPSILON。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。等于 2 的 -52 次方。Number.EPSILON === Math.pow(2, -52)

    function withinErrorMargin (left, right) {
      return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
    }
    
    0.1 + 0.2 === 0.3 // false
    withinErrorMargin(0.1 + 0.2, 0.3) // true
    
    1.1 + 1.3 === 2.4 // false
    withinErrorMargin(1.1 + 1.3, 2.4) // true
    

    上述代码表示,如果浮点计算误差在 2的-50次方 内,就认为是相等的。

    在js中,无论对整数还是小数,都是有最大上限和下限的,都有对应的常量表示。超过最大值后,js就类似这样处理了9007199254740993 === 9007199254740992; // true

    持续更新中。。。

    相关文章

      网友评论

          本文标题:(持续更新...)ES6学习笔记(一)

          本文链接:https://www.haomeiwen.com/subject/wattvftx.html