• YDKJ 读书笔记 01 Function vs. Block Scope


    Introduction

    本系列文章为You Don't Know JS的读书笔记。
    书籍地址:https://github.com/getify/You-Dont-Know-JS

    Scope From Functions

    一个非常普遍的观点是,Javascript的作用域是基于函数的,这个观点其实并不是那么正确,不过,让我们来先看一下函数级别的作用域。

    function foo(a) {
        var b = 2;
        // some code
        function bar() {
            // ...
        }
        // more code
        var c = 3;
    }
    

    foo函数中有4个标识符,a、b、c、bar。这些标识符在什么位置定义并不重要,变量或者是函数,都将归属于当前的作用域。
    bar()也有与之对应的作用域,全局作用域也是如此(仅有一个表示符foo与它关联)。
    a,b,c,bar都隶属于foo(..)作用域,在foo(..)的外部,是无法操作到它们的。在全局作用域下写如下代码:会抛出ReferenceError

    bar(); // fails
    console.log( a, b, c ); // all 3 fail
    

    Functions As Scopes

    使用函数作为代码的封装,有一些细节上的问题:我们使用foo(..)封装代码时,foo本身也污染了作用域。如果我们只是为了隐藏细节,不是为了抽象代码逻辑,那么,这是一个解决方案:

    (function foo(){ 
        // Coding here
    })();
    

    这样做,foo是污染不了全局作用域的(也许我们更愿意在这种情况下采用匿名版本)。

    (function (){ 
        // Coding here
    })();
    

    这里说一下使用匿名函数的两个坏处:无法在stack traces中识别到他们、低代码可读性。
    所以除了较短的逻辑,尽量不要用匿名函数。

    Invoking Function Expressions Immediately

    刚才我们通过把函数包装在(..)中,再立即调用,被称为Immediately Invoked Function Expression,简称为IIFE,为了避免匿名函数的坏处,IIFE可以这样命名

    var a = 2;
    (function IIFE(){
    
        var a = 3;
        console.log( a ); // 3
    
    })();
    console.log( a ); // 2
    

    关于客户端的IIEF,经常可以看到这样的代码,传window,做些事情。

    var a = 2;
    (function IIFE( global ){
    
        var a = 3;
        console.log( a ); // 3
        console.log( global.a ); // 2
    })( window );
    

    还有这样的,比较暗黑的做法(防止某变量被外界覆盖,比如undefined):

    undefined = true; // setting a land-mine for other code! avoid!
    
    (function IIFE( undefined ){
        var a;
        if (a === undefined) {
            console.log( "Undefined is safe here!" );
        }
    })();
    

    Blocks As Scopes

    for (var i=0; i<10; i++) {
        console.log( i );
    }
    console.log(i);
    

    很多从其它语言切换切换到js,不能理解以上的代码居然可以输出10。
    其它语言常用到的块级作用域,在js上似乎没有,其实不然。
    try/catch

    try {
        undefined(); // illegal operation to force an exception!
    }
    catch (err) {
        console.log( err ); // works!
    }
    console.log( err ); // ReferenceError: `err` not found
    

    注意到没有,catch是能拿到err,但是log却拿不到。

    虽然这是ES3的规范,但是很多语法验证工具会在有多个try/catch,且catch的形参都为err或者其它相同的名字时,会报re-definition的warning,不用管。

    let
    ES6提供了新的关键字let。

    var foo = true;
    if (foo) {
        let bar = foo * 2;
        bar = something( bar );
        console.log( bar );
    }
    console.log( bar ); // ReferenceError
    

    块级风格。

    for (let i=0; i<10; i++) {
        console.log( i );
    }
    console.log( i ); // ReferenceError
    

    需要注意的是,如果使用let

    {
       console.log( bar ); // ReferenceError!
       let bar = 2;
    }
    

    在这一点上,和var是不一样的。

    const

    const也是ES6提供的新关键字,除了定义出的变量初始化后不可更改外,其它与let一致。

  • 相关阅读:
    LNMP架构三
    LNMP架构二
    LNMP架构
    LAMP架构三
    LAMP架构二
    LAMP架构
    rsync工具介绍
    mysqldump备份单表数据
    阿铭每日一题 day 14 20180125
    阿铭每日一题 day 13 20180124
  • 原文地址:https://www.cnblogs.com/E-WALKER/p/4782475.html
Copyright © 2020-2023  润新知