zoukankan      html  css  js  c++  java
  • ES6标准入门 第二章:块级作用域 以及 let和const命令

    一、块级作用域

      1、为什么需要块级作用域?

      ES5中只有全局作用域和函数作用域,带来很多不合理的场景。

      (1)内层变量可能会覆盖外层变量;   

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

      变量提升导致了内层的tmp变量覆盖了外层的tmp变量。

      (2)用来计数的循环变量泄露为全局变量;

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

     

      2、ES6的块级作用域:

      (1)let为JavaScript新增了块级作用域;外层代码不受内层代码的影响。

    function f() {
        let n=5;
        if(true) { let n=10; }
        console.log(n);   //5      
    }

      (2)ES6允许块级作用域任意嵌套;

    {{{ 
         {let insane = "hello world"}
         console.log(insane);  // 报错       
    }}}

      (3)内层作用域可以定义外层作用域的同名变量;

    {{{ 
         {let insane = "hello world"}
         let insane = "hello world"      
    }}}

      (4)块级作用域 与 函数声明;

      函数本身的作用域在其所在的块级作用域之内;

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

      上述代码在ES5中运行,会得到 I am inside!   因为ES5 存在变量声明提升,不管进不进入if代码,在if内声明的函数f会被提升到当前作用域的顶部 ;

    // ES5 环境
    function f() { console.log('I am outside!'); }
    (function () {
      function f() { console.log('I am inside!'); }  //提升到当前作用域的顶部
      if (false) {
      }
      f();
    }());

      但是在ES6中运行,会得到 I am outside!  因为ES6 块级作用域内声明的函数类似于let,对作用域之外没有影响。

      但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的,!!!!!

      ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
        1、允许在块级作用域内声明函数。
        2、函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
        3、同时,函数声明还会提升到所在的块级作用域的头部。
      注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。
     
    // 浏览器的 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

      因此,考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。

    二、let 命令

      1、仅在块级作用域内有效; 【for循环的计数器 很适合用let命令】

      let 命令用于声明变量, 与var类似, 但是所声明的变量只在let命令的代码块内有效,不受外部影响。

    {
        let a = 10;
        var b = 20;     
    }
    a //a is not defined
    b //20

      2、不存在变量提升;变量一定在声明后使用,否则会报错。

    console.log(foo);   //报错
    let foo = 5;

      3、暂时性死区;

      在代码块内,使用let命令声明变量之前,该变量都是不可用的,语法上称为暂时性死区(temporal dead zone 简称TDZ)

    if (true) {
      // TDZ开始
      tmp = 'abc'; // ReferenceError
      console.log(tmp); // ReferenceError
      let tmp;  // TDZ结束
    
      console.log(tmp); // undefined
      tmp = 123;
      console.log(tmp); // 123
    }

      暂时性死区的本质:只要已进入当前作用域,变量就已经存在了,但是不可获取;只有等到声明变量的那一行代码出现,就可以获取和使用该变量。

      以下是一些比较隐蔽的“死区”: 

    function bar (x=y, y=2) {
          return [x,y];
    }
    bar(); // 报错   此时x的默认值为参数y,而y此时还没有声明,属于“死区”
    function bar (x=2, y=x) {
          return [x,y];
    }
    bar(); //[2,2]

      4、不允许重复声明;

    //报错
    function(){
        let a=1;
        var a=2;    
    }
    //报错
    function(){
        let b=1;
        let b=2;    
    }
    //报错
    function fun(arg){
       let arg; //报错 不能再函数内部重新声明参数
    }

     

    三、const命令

      1、const用来声明常量, 一旦声明其值就不会改变;

    const PI = 3.1415;
    PI // 3.1415
    PI = 3;
    // TypeError: Assignment to constant variable.

      2、const一旦声明就必须立即初始化;

    const foo;
    // SyntaxError: Missing initializer in const declaration

      3、const只在声明所在的块级作用域内有效;

      4、const声明的常量也不会提升,同样存在暂时性死区,只能先声明后使用;

      5、const也不可以重复声明常量;

      6、const本质: 

        const实际保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。

        对于简单数据类型(数字,字符串,布尔值),值就保存在变量指向的那个内存地址,因此等同于常量;

        对于复合类型的数据(对象,数组),变量指向的内存地址,const只能保证这个地址是不变的,至于它指向的数据结构是不是可变就不能控制了。

    const foo = {};
    // 为 foo 添加一个属性,可以成功
    foo.prop = 123;
    foo.prop // 123
    // 将 foo 指向另一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only

      7、若想冻结对象,应该使用object.freeze方法;

    const foo = Object.freeze({});
    
    // 常规模式时,下面一行不起作用;
    // 严格模式时,该行会报错
    foo.prop = 123;

      8、冻结对象本身以及对象属性:

    var constantize = (obj) => {
        Object.freeze(obj);
        Object.keys(obj).forEach( (key, value)=> {
            if( typeof obj[key] === 'object' ) {
                constantize( obj[key] );   // 递归
            }
        } )          
    }

      9、ES6声明变量的六种方法:

        ES5提供了两种: var命令 、function命令

        ES6新增了四种: let命令、 const命令、 import命令、 class命令

    四、全局对象的属性

      全局对象 又称顶层对象,在浏览器环境指的是window对象, 在Node.js中指的是global对象。

      在ES5中,全局对象的属性 和 全局变量是等价的;

    window.a = 9;
    a  // 9
    
    b=6;
    window.b  // 6

      ES6对此做了新的规定:

       1、考虑兼容性,var和function声明的全局变量 依旧是全局对象的属性;

       2、let、const、class 声明的全局变量 不属于全局对象的属性;

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

    五、全局对象(顶层对象)的扩展:

      ES5的顶层对象,在各种实现里是不统一的:

        浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。

        浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。

        Node 里面,顶层对象是global,但其他环境都不支持。 

        下面有两种勉强的方法,使同一段代码能够在各种环境,都能取到顶层对象:

    //方法一:
    (   typeof window != 'undefined' 
            ? window 
            : ( typeof process === 'object' &&
                 tyoeof require === 'function' &&
                 typeof global === 'object'
              )
              ? global : this
    );        
    
    // 方法二:
    var getGlobal = function() {
          if(typeof self !== 'undefined') { return self }
          if(typeof window !== 'undefined') { return window } 
          if(typeof global !== 'undefined') { return global } 
          throw new Error('unable to locate global object');
    }
  • 相关阅读:
    day02_1spring3
    day01_2spring3
    动态代理的介绍
    day04_1hibernate
    day03_2hibernate
    Oracle11gR2安装完成后不手动配置监听的使用方法
    css的样式和选择符的优先权
    调用css时,link和@import url的区别
    jquery 获取和修改img标签的src属性
    正则表达式实现6-10位密码由数字和字母混合组成
  • 原文地址:https://www.cnblogs.com/james23dong/p/8466820.html
Copyright © 2011-2022 走看看