zoukankan      html  css  js  c++  java
  • 块级作用域绑定

    #var声明及变量提升机制
        在 函数作用域 或 全局作用域中通过 var 声明的变量,无论实际上在哪里声明的,都会被当成在  当前作用域顶部声明的变量。这就是常说的提升机制;
    function func(condition){
        if(condition){
            var value="xxx";
            return value;    
        }
        else{
            //这里可以访问 value,其值为 undefined
            return null;
        }
        //此处可以访问 value,其值为 undefined
    }
        上面的代码,在预编译阶段,JavaScript引擎会将上面的func函数修改成下面这样:
    function func(condition){
        var value;
        if(condition){
            value="xxx";
            return value;    
        }
        else{
            return null;
        }
    }
        
        变量 value 的声明被提升至函数顶部,而初始化操作依旧在原处执行,这就意味着在 else 子句中可以访问到该变量,且由于没有被初始化,所以其值为 undefined;
        正因为这个问题,ECMAScript 6 引入了块级作用域来强化对变量声明周期的控制
    #块级声明
        块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域存在于:
            1,函数内部;
            2,块中(花括号{}之间);
        
        &let声明
            let用法和var相同。用let来替代var来声明变量,可以把变量的作用域限制在当前代码块中。
    function func(condition){
        if(condition){
            let value="xxx";
            return value;    
        }
        else{
            //变量 value 在此处不存在
            return null;
        }
        //变量 value 在此处不存在
    }
        
            变量 value 用let声明后,不再被提升至函数顶部。执行流离开if块,value 立刻被销毁。如果condition为false,就永远不会声明并初始化 value。

        &禁止重声明

            假设作用域中已经存在某个变量,此时再使用 let 关键字声明一次就会错误:
    var count = 30;
    // 抛出语法错误
    let count = 40;
       

        &const声明

            ECMAScript 6 标准还提供了 const 关键字。使用声明的是常量,其值一旦被设定后不可更改。因此,每个通过 const 声明的常量必须进行初始化
    // 有效的常量
    const maxNum = 30;
    // 语法错误:常量未初始化
    const minNum;
        const 与 let 声明的都是块级标识符,所以常量也只在当前代码块内有效,一旦执行到块外会立即被销毁。常量同样也不会被提升至作用域顶部;
        与 let 相似,在同一作用域中用 const 声明已经存在的标识符也会导致语法错误;
     

        &用 const 声明对象

            记住,const 声明不允许修改绑定,但是允许修改值。这也就意味着用 const 声明对象后,可以修改该对象的属性值,但是不可以修改该对象的引用:
    const person = {
        name : 'zhangsan'
    }
    // 没毛病
    person.name = 'lisi';
    // 抛出语法错误
    person = {
        name : 'wangwu'
    }
     

    #循环中的块级作用域绑定

        以下的代码在JavaScript中很常见:
    for(var i=0;i<10;i++){
        process(arr[i]);
    }
    console.log(i);    // 打印10
        由于 var 声明得到提升,变量 i 的声明提升至当前作用域顶部,在循环结束后依旧可以访问它;如果换成 let 声明变量:
    for(let i=0;i<10;i++){
        process(arr[i]);
    }
    // i 在这里不可访问,抛出一个错误
    console.log(i);    
        
        
        &循环中的函数
            var 声明让开发者在循环中创建函数变得异常困难,对于新手会造成困惑,看下面的代码:
    var funcs = [];
    for(var i=0;i<10;i++){
        funcs.push(function(){
            console.log(i);    
        });
    }
    funcs.forEach(function(func){
        func();
    });
        
            预期的结果是输出数字 0 ~ 9,但是程序却一直输出 10;
            这是因为循环里的每次迭代同时共享着变量 i,循环内部创建的函数全部保留了对相同变量的引用。当循环结束时候,变量 i 的值为10,所以每次调用 console.log(i) 时就会输出10;
            为了解决上面的问题,可以在循环中使用 立即调用函数表达式(IIFE),以强制生成计数器变量的副本:
    var funcs = [];
    for(var i=0;i<10;i++){
        funcs.push(function(value){
            return function(){
                console.log(value);
            }  
        });
    }
    funcs.forEach(function(func){
        func();    //依次输出 0 ~ 9
    });
            在循环内部,IIFE 表达式为接受的每一个变量 i 都创建了一个副本并存储为变量 value。这个变量的值就是相应迭代创建的函数所使用的值。
            ECMAScript 6 中的 let 和 const 提供的块级绑定就无需这么麻烦了;
        
        &循环中的let声明
            let 声明模仿上面的 IIFE 所做的一切来简化循环过程。
    var funcs = [];
    for(let i=0;i<10;i++){
        funcs.push(function(){
            console.log(i);
        });
    }
    funcs.forEach(function(func){
        func();    //依次输出 0 ~ 9
    });
        
        &循环中的const
            ES6 标准中没有明确指明不允许在循环中使用 const 声明,然而,针对不同类型的循环它会表现出不同的行为。
            对于普通的 for 循环,使用 const 声明计数器,那肯定是会抛出错误的:
    var funcs = [];
    // 循环一次之后,抛出错误
    for(const i=0;i<10;i++){
        //...
    }
            
            但是在 for-in 或者 for-of 循环中使用 const 声明的行为与使用 let 是一致的:
    var funcs = [];
    var obj = {
        a:true,
        b:true,
        c:true
    };
    // 不会产生错误
    for(const key in obj){
        funcs.push(function(){
            console.log(key);
        })
    }
    funcs.forEach(function(func){
        func();    // 依次输出 a,b,c
    })
            之所以可以在 for-infor-of 中使用 const 声明,是因为每次迭代不会修改已有的绑定,而是会创建一个新的绑定
     

    #全局作用域绑定

        let 和 const 与 var 的另外一个是它们在全局作用域中的行为;
        当 var 被用于全局作用域时,它会创建一个新的全局变量作为全局对象(浏览器环境下全局对象为window)的属性。这也就意味着 var 可能会在无意中覆盖一个已经存在的全局变量
    // 在浏览器中
    var RegExp = 'Hello!';
    console.log(window.RegExp);    // 输出 Hello!
    var ncz = 'Hi!';
    console.log(window.ncz);
        
        即使全局对象 RegExp 对象定义在 window上,也不能幸免于被 var 声明覆盖掉;
        如果在全局作用域中使用 let 或 const,会在全局作用域下创建一个新的绑定,但该绑定不会添加为全局对象的属性。换句话说,用 let 或 const 不会覆盖全局变量,而只能遮蔽它;
    // 在浏览器中
    let RegExp = 'Hello!';
    console.log(RegExp);    // 输出 Hello!
    console.log(window.RegExp === RegExp);    // false
    const ncz = 'Hi!';
    console.log(ncz);    // 输出 Hi!
    console.log("ncz" in window);    // false
        let 声明的 RegExp 创建了一个绑定并遮蔽了全局的 RegExp 变量,结果就是 window.RegExp 和 RegExp 不相同,但不会破坏全局作用域。
        同样,const 声明的 ncz 创建了一个绑定但没有创建为全局对象的属性。
        如果不想为全局对象创建属性,则使用 let 或 const 要安全得多。
        tips:在浏览器中跨 frame 或者跨 window 访问,依然得使用 var在全局对象下定义变量。
     

    #块级绑定最佳实践

        当更多的开发者迁移到ES6后,一种做法日益普及:默认使用 const ,只有确实需要改变变量的值时使用 let。
        因为大部分变量的值在初始化后不应再改变,而预料外的变量值的改变是很多 bug 的源头。
  • 相关阅读:
    解决在SQLPLUS中无法使用方向键、退格键问题
    Oracle 11g R2手动配置EM(转)
    为什么JDK代码这样写?final ReentrantLock takeLock = this.takeLock
    使用CompletableFuture实现业务服务的异步调用实战代码
    SpringBoot项目从Git拉取代码并完成编译打包启动的sh自动脚本
    SpringBoot项目实现配置实时刷新功能
    (8)Flask微电影项目会员中心其他页面搭建
    (7)Flask微电影之会员中心页面搭建
    (6)Flask项目之会员注册页面
    (5)Flask项目会员登录页
  • 原文地址:https://www.cnblogs.com/startcaft/p/7712504.html
Copyright © 2011-2022 走看看