Lyrics


< More and better />


es6

let 和 const 变量

  • let 没有变量提升(var 有), let 必须先声明变量才能使用

    使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为 “暂时性死区”

    let 声明的变量只在所声明的作用域内有效

  • 不允许在同一个作用内重复声明(let) 变量 ;

for 循环

for 循环,循环 语句是一个作用域,内部的语句是一个作用域


    for (let i = 0; i < 3; i++) {
          let i = 'abc';
          console.log(i);
    }
    输出:    // abc
        // abc
        // abc

见解: 内部作用域的i 和循环的 i 不在一个作用域内,所以不影响

es5 没有块级作用域

  • es5 没有块级作用域,只有函数作用域和全局作用域

影响: 内层的变量会影响(覆盖)外部的变量


    var tmp = new Date();

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

    f(); // undefined

见解 : 在函数作用域内 ,( tmp = new Date();在函数外,所以没有指向一个地址)

因为var 有变量提升

所以在没有执行if 语句的给tmp赋值之前,tmp = undefined;

所以输出 undefined ,则覆盖了外部的 tmp ;

循环体计数的变量泄露为全局变量


    var s = 'hello';

    for (var i = 0; i < s.length; i++) {
      console.log(s[i]);
    }

    console.log(i); // 5


见解: i 本身应该属于 循环体内,但是没有跨级作用域,所以 i 变成全局变量

es5 可以可以再块级作用域中声明函数吗,在外部引用是对的

es6 的块级作用域

  • let 变量使得有了块级作用域

  • 一个{}内属于一个作用域

  • 内层的作用域,和外层作用域可以定义相同的变量

  • es6 不可以再块级作用域中声明函数,在外部引用是有错误的

    但是浏览器可以有自己的行为:

    • 允许在块级作用域内声明函数

    • 函数声明会用变量提升原则(提升到全局作用域的头部)

    • 同时函数提升在会提升到所在块级作用域的头部

es6 函数声明在块级作用域内有效在只有使用大括号的前提下成立


    // 不报错
    'use strict';
    if (true) {
      function f() {}
    }

    // 报错
    'use strict';
    if (true)
      function f() {}

do 表达式

可以使得块级作用域有返回值 ,在块级作用域前加上 do ,使得变成 do 表达式


    let x = do {
      let t = f();
      t * t + 1;
    };

见解 : X 会得到整个块级作用域的返回值

const 变量 (不变的象征)

本质上 不是const (声明的常量)变量的值不可改变,而是所指向的内存地址不可改变

对于简单的数据类型 (数值,字符串,布尔值) ,只是指向那个变量所存的地址

但是对于复合类型(对象和数组) ,只是能保证指针指向 不变 但是没有办法保证数据

结构不变(就是给对象添加属性等)


    const foo = {};

    // 为 foo 添加一个属性,可以成功
    foo.prop = 123;
    foo.prop // 123

    // 将 foo 指向另一个对象,就会报错
    foo = {}; // TypeError: "foo" is read-only


见解 : 指针指向不变(地址),但是对象本身是可变的


    const a = [];
    a.push('Hello'); // 可执行
    a.length = 0;    // 可执行
    a = ['Dave'];    // 报错


见解 : 指针指向不变(地址),但是对象本身是可变的,但是如果将一个数组赋值给 a 则是错误的;

声明常量必须非常小心

  • 意味着 一旦某个变量使用 const声明 ,则 必须直接在声明的同时赋值

    不然是不可以之后赋值的(一定意义上的修改)

  • 和 let 一样,只在块级作用域内有效

  • 没有变量提升 ,也存在暂时性死区(在变量没有声明之前是不可以使用的)

  • 不可重复声明变量(和 let 一样)

对象冻结

Object.freeze


    const foo=object.freeze({});
    foo.prop=123;
    //常规模式上一句不起作用
    // 严格模式下,会报错

冻结对象以及冻结对象的属性


    ** 将对象彻底冻结的函数。 **
    var constantize = (obj) => {
      Object.freeze(obj);
      Object.keys(obj).forEach( (key, i) => {
        if ( typeof obj[key] === 'object' ) {
          constantize( obj[key] );
        }
      });
    };


见解 :声明函数constantize ,指向 obj ,先是冻结对象,然后利用循环冻结对象属性(调用函数)

声明变量

  • es5 声明变量

    只有两种办法 var 和 function

  • es6声明变量

    var 和function ,let ,const,import,class

顶层对象的属性(浏览器 :window; Node :global)

  • es5

    顶层对象和全局变量一样

  • es6
    var ,window声明的变量属于顶层对象,但是const,let ,class,属于局部变量

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

    见解:顶层对象(全局变量和局部变量在分解 开)

global 对象

  • 因为各个语言不同的区别 目前使用 this 取到顶层对象

  • 在全局环境中this 指向 顶层对象,但是this 一般返回的是所在模块的对象

  • 函数中的this ,如果不是以对象方法运行,而是只是以函数运行,this 指向顶层对象,严格模式下为 undefined;

  • 无论是在严格模式下,还是普通模式下,new function(‘return this’)();总是返回全局对象

    但是如果浏览器使用了 CSP(安全模式),eval(),new function()方法都无法使用

    自己认为是因为(是防止网络非法攻击)

很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法


    (typeof window != 'undefined '
    ?  window
    :(typeof process === 'object' && 
     typeof require === 'object' && 
     typeof global==='object') 
     ? global
     :this);

** 见解 :检测是window 对象是否等于undefined,是的话取window

,反之检测是不是属于以下的三种类型,属于的话

,取global 对象,反之取this 对象  **

数组的解构赋值

简单地来说就是模式匹配一致的情况下进行赋值

  • 完全解构

    
          let [foo, [[bar], baz]] = [1, [[2], 3]];
          foo // 1
          bar // 2
          baz // 3
          //完全解构
      
  • 不完全解构

    
          let [x, y] = [1, 2, 3];
          x // 1
          y // 2
          //不完全解构
      
  • 解构不成功

    
          let [foo] = [];
          let [bar, foo] = [1];
          //解构不成功
          //foo的值为undefined
      
  • 使用set,使用数组方式 解构

Set是ES6引入的集合类,具备Iterator(迭代器)接口,可以用于解构赋值


let [x,y,z] = new set([‘a’,’b’,’c’]);
//x=a,y=b,z=c;
let foo={};
// 空对象不具备Iterator接口

</code></pre>
  • 具有 迭代器的数据结构,都可以采用数组解析的方法赋值

    
        // Generator是ES6引入的函数类型,具备Iterator接口,可以用于解构赋值
        // yield在Generator中相当于return,后续文章会详细说明。
        function* fibs() {
          var a = 0, b = 1;
          while (true) {
            yield a;
            [a, b] = [b, a + b];    // 函数内部的解构赋值。
          }
        }
        let [first, second, third, fourth, fifth, sixth] = fibs();    // sixth: 5
    
  • 解构赋值允许默认值

    
    
    
    let [x, y = 'b'] = ['a']; // x='a', y='b'
    let [x, y = 'b'] = ['a', undefined]; // x='a', y='b
    

但是在 es6中使用严格相等运算符(===),所以如果所赋值不是等于 undefined ,则默认值不算

<pre><code>
    let [x = 1] = [undefined];
    x // 1

    let [x = 1] = [null];
    x // null
</code></pre>

        ** 默认值是表达式 **
        <pre><code>
                function f() {
      console.log('aaa');
    }

    let [x = f()] = [1];
    //当1 == udefined时才会计算f();
    //否则直接取1(惰性赋值)
        </code></pre>

**  默认值是变量 (前提是变量已经声明过)**

对象的默认解构

  • 对象的解构不是像数组解构一样具有次序,是没有次序的,所以在解构赋值的同时应该保证变量必须与属性同名

    
         let { bar, foo } = { foo: "aaa", bar: "bbb" };
        foo // "aaa"
        bar // "bbb"
    
        let { baz } = { foo: "aaa", bar: "bbb" };
        baz // undefined
     
  • 如果变量名和属性名不同名 必须写成下面那样

    
         var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
        baz // "aaa"
    
        let obj = { first: 'hello', last: 'world' };
        let { first: f, last: l } = obj;
        f // 'hello'
        l // 'world'
     
  • 解构赋值的变量都会重新声明,所以使用let 和 const 变量进行解构赋值时应该注意,不要提前声明变量

    
          let tmp;
          let{foo:tmp} = {foo:'aaa'};//SyntaxError: Duplicate declaration "tmp";
          //重复声明变量的错误
    
          let foo;
          ({foo} = {foo: 1}); // 成功
    
          let baz;
          ({bar: baz} = {bar: 1}); // 成功
          //解析器会将 第一个大括号理解成为代码块
          所以就是不同的作用域
          就不存在重复定义
      
  • 解构允许嵌套结构的对象 和 数组一样。

    
    
          let obj = {
            p: [
              'Hello',
              { y: 'World' }
            ]
          };
    
          let { p: [x, { y }] } = obj;
          x // "Hello"
          y // "World"
      
  • 也具有默认值解构赋值 (和数组一样)

  • 对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量

    
         let { log, sin, cos } = Math;
         //使得获得Math对象的对数、正弦、余弦三个方法,
     

    由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

    
         let arr = [1, 2, 3];
         let {0 : first, [arr.length - 1] : last} = arr;
         first // 1
         last // 3    
     

字符串的解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

数值和布尔值解构赋值

  • 解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

    
          let {toString: s} = 123;
          s === Number.prototype.toString // true
    
          let {toString: s} = true;
          s === Boolean.prototype.toString // true
      

    见解 :数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

  • 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

    
         let { prop: x } = undefined; // TypeError
         let { prop: y } = null; // TypeError    
     

函数参数的解构赋值

基本和数值解构一样

<pre><code>

    [[1, 2], [3, 4]].map(([a, b]) => a + b);
    // [ 3, 7 ]
</code></pre>

<pre><code>
    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]
    //为变量x和y指定默认值
</code></pre>

<pre><code>
    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的参数指定默认值
</code></pre>

圆括号问题

不允许使用圆括号的情况

  • 在声明语句中

  • 在函数参数中

  • 赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号之中。

    就是一边使用圆括号,一边不使用圆括号

    
          // 全部报错
          ({ p: a }) = { p: 42 };
          ([a]) = [5];
      

可以使用圆括号的情况

  • 赋值语句的非模式部分,可以使用圆括号(就是不存在属相名:属性值)这种的

变量解构赋值的用途

  • 交换变量的值

  • 从函数返回多个值

    函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

    
          // 返回一个数组
    
          function example() {
            return [1, 2, 3];
          }
          let [a, b, c] = example();
    
          // 返回一个对象
    
          function example() {
            return {
              foo: 1,
              bar: 2
            };
          }
          let { foo, bar } = example();
      
  • 函数参数的定义(就是函数的参数可以是多个解构对象,并且可以很清晰的了解每个变量的赋值)

    
          // 参数是一组有次序的值
          function f([x, y, z]) { ... }
          f([1, 2, 3]);
    
          // 参数是一组无次序的值
          function f({x, y, z}) { ... }
          f({z: 3, y: 2, x: 1});
      
  • 提取 json 数据

    <pre><code>
        let jsondata = {
        id: 1,
        status : 1,
        date :[100,200]
        };
        let {id,status,date:number} = jsondata;
        //id:1
        //status : 1
        // number :[100,200]
    </code></pre>
    
  • 遍历 map 解构

    具有迭代器接口的对象都可以使用 for of 循环得到,健值和 value ,所以
    可以很方便

字符串

  • 允许unicode编码

  • js 内部编码是以 utf - 16存储的,每个字符规定2 个 字节

  • 所以使用