《你不知道的JavaScript - 上》之函数作用域和块作用域

92 阅读3分钟

文章内容全部来自《你不知道的JavaScript - 上》,记录学习呀呀呀!!!

函数作用域

函数作用域是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。

var a = 2; //属于全局
function foo() { 
    var a = 3;  //属于foo这个函数
    console.log(a); // 3 
} 
foo(); 
console.log(a); // 2
匿名、具名、立即执行

匿名与具名

setTimeout(function() { 
    console.log("I waited 1 second! "); 
}, 1000 );

这叫作匿名函数表达式,因为function()..没有名称标识符。函数表达式可以是匿名的,而函数声明则不可以省略函数名——在JavaScript的语法中这是非法的。

匿名函数的缺点:

  • 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。
  • 如果没有函数名,当函数需要引用自身时只能使用已经过期的arguments.callee引用,比如在递归中。
  • 匿名函数省略了对于代码可读性/可理解性很重要的函数名。一个描述性的名称可以让代码不言自明。

立即执行函数

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

上述模式(function foo())(),专业术语:IIFF,代表立即执行函数表达式(Immediately Invoked Function Expression)

两种书写形式:

  • 函数表达式被包含在()中,然后在后面用另一个()括号来调用,(function foo(){ .. })()
  • 函数表达式被包含在()中,然后在后面用另一个()括号来调用,(function(){ .. }())

应用场景

  1. 当作函数调用并传递参数进去
var a = 2; 
(function IIFE(global) { 
    var a = 3; 
    console.log(a); // 3 
    console.log(global.a); // 2 
})(window); 
console.log(a); // 2
  1. 解决undefined标识符的默认值被覆盖导致的异常;将一个参数命名为undefined,但是在对应的位置不传入任何值,这样就可以保证在代码块中undefined标识符的值真的是undefined
(function IIFE(undefined) { 
    var a; 
    if (a === undefined) { 
     console.log("Undefined is safe here! "); 
    } 
})();

区分函数声明和表达式最简单的方法是看function关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式

块作用域

举个例子🌰


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

====等于如下代码====

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

实际上,代码中只需要让i出现在for循环内部,但是变量i污染到了整个作用域,这样的话就很容易引起变量的冲入或者其他意想不到的错误。

let

ES6为改变现状,引入了新的关键字let。let关键字可以将变量绑定到所在的任意作用域中(通常是{ .. }内部)。换句话说,let为其声明的变量隐式地劫持了所在的块作用域。

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

let将bar隐士的绑定在了{}块级作用域中
const

除了let以外,ES6还引入了const,同样可以用来创建块作用域变量,但其值是固定的(常量)。之后任何试图修改值的操作都会引起错误。

var foo = true; 
if (foo) { 
    var a = 2; 
    const b = 3; // 包含在if中的块作用域常量
    a = 3; // 正常! 
    b = 4; // 错误! 
} 
console.log(a); // 3 
console.log(b); // ReferenceError!

特殊

try/catch

JavaScript的ES3规范中规定try/catch的catch分句会创建一个块作用域,其中声明的变量仅在catch内部有效

try { 
    undefined(); // 执行一个非法操作来强制制造一个异常 
} catch (err) { 
    console.log(err); // 能够正常执行! 
} 
console.log(err); // ReferenceError: err not found

with

with从对象中创建出的作用域仅在with声明中而非外部作用域中有效。