
function foo(a) {
var b = 2;
function bar() {
// ...
}
var c = 3;
}bar(); // ReferenceError: bar is not defined
console.log(a, b, c); // 全都抛出 ReferenceError属于这个函数的全部变量都可以在整个函数的范围内使用及复用(在嵌套的作用域中也可以使用)。这种设计方案可根据需要改变值类型的 "动态" 特性。反过来可带来一些启示:从所写的代码中挑选出一个任意片段,然后就用函数声明的方式对它进行包装,实际上就是把这些代码 "隐藏" 起来了。实际的结果就是在这个代码片段的周围创建了一个新的作用域,也就是说这段代码中的任何声明(变量或函数)都将绑定在这个新创建的函数作用域中,而不是先前所在的作用域中。换句话说,可把变量和函数包裹在一个函数的作用域中,然后用这个作用域来 "隐藏" 他们。function doSomething(a) {
b = a + doSomethingElse( a * 2 );
console.log( b * 3 );
}
function doSomethingElse(a) {
return a - 1;
}
var b;
doSomething( 2 ); // 15function doSomething(a) {
function doSomethingElse(a) {
return a - 1;
}
var b;
b = a + doSomethingElse( a * 2 );
console.log( b * 3 );
}
doSomething( 2 ); // 15"隐藏" 作用域中的变量和函数的另一个好处是可避免同名标识符的冲突,两个标识符名字相同但用途不同,无意间可能会造成命名冲突,而冲突会导致变量的值被意外覆盖。function foo() {
function bar(a) {
i = 3; // 修改for 循环所属作用域中的i
console.log( a + i );
}
for (var i=0; i<10; i++) {
bar( i * 2 ); // 糟糕,无限循环了!
}
}
foo();var MyReallyCoolLibrary = {
awesome: "stuff",
doSomething: function() {
// ...
},
doAnotherThing: function() {
// ...
}
}2. **模块管理**var a = 2;
function foo() { // <-- 添加这一行
var a = 3;
console.log( a ); // 3
} // <-- 以及这一行
foo(); // <-- 以及这一行
console.log( a ); // 2var a = 2;
(function foo() {
// <-- 添加这一行
var a = 3;
console.log(a); // 3
})(); // <-- 以及这一行
console.log(a); // 2(function... 而不仅是以 function... 开始。函数会被当做函数表达式而不是一个标准的函数声明来处理。function 关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 为声明中的第一个关键字,那它就是一个函数声明,否则就是一个函数表达式。函数声明和函数表达式之间最重要的区别就是他们的名称标识符将会绑定在何处。(function foo(){...}) 作为函数表达式意味着 foo 只能在 ... 所代表的位置中被访问,外部作用域则不行。setTimeout(function () {
console.log("I waited 1 second!");
}, 1000);匿名函数表达式,因为 function().. 没有名称标识符。函数表达式可以是匿名的,而函数声明则不可以省略函数名——在JavaScript 的语法中这是非法的。匿名函数在栈追踪中不会显示出有意义的函数名,这使调试很困难。过期的 arguments.callee 来引用。代码可读性不是很友好。setTimeout(function timeoutHandler() {
console.log("I waited 1 second!");
}, 1000);var a = 2;
(function IIFE() {
var a = 3;
console.log(a); // 3
})();
console.log(a); // 2var a = 2;
(function IIFE() {
var a = 3;
console.log(a); // 3
}());
console.log(a); // 2把他们当做函数调用并传递参数进去,如下:var a = 2;
(function IIFE(global) {
var a = 3;
console.log(a); // 3
console.log(global.a); // 2
})(window);
console.log(a); // 2解决 undefined 标识符的默认值被错误覆盖导致的异常。undefined = true; // 给其他代码挖了一个大坑!绝对不要这样做!
(function IIFE(undefined) {
var a;
if (a === undefined) {
console.log("Undefined is safe here!");
}
})();倒置代码的运行顺序,将需要运行的函数放在第二位,在IIFE执行之后当做参数传递进去。var a = 2;
(function IIFE(def) {
def(window);
})(function def(global) {
var a = 3;
console.log(a); // 3
console.log(global.a); // 2
});for (var i = 0; i < 5; i++){
console.log(i);
}var foo = true;
if(foo) {
var bar = foo * 2;
bar = something(bar);
console.log(bar);
}用 with 从对象中创建出的作用域仅在 with 所处作用域中有效。try/catch 的 catch 分句会创建一个块作用域,其中声明的变量仅会在 catch 内部有效。try {
undefined(); // 目的是让他抛出一个异常
} catch (error) {
console.log("error ------>", error); // TypeError: undefined is not a function
}
console.log("error ------>", error); // ReferenceError: error is not definederror 仅存在于 catch 分句内部,当视图从别处引用它时会抛出错误。let 关键字将变量绑定到所处的任意作用域中(通常是 { ... } 内部)。换句话说,let 声明的变量隐式地了所在的块作用域。var foo = true;
if(foo) {
var bar = foo * 2;
bar = something(bar);
console.log(bar);
}
console.log(bar); // ReferenceError: bar is not defined使用 let 进行的声明不会再块作用域中进行提升。声明的代码被运行前,声明并不 "存在"。{
console.log(bar); // ReferenceError
let bar = 2;
}1. 垃圾收集
function process(data) {
// do something
}
var someObj = {};
process(someObj);
var btn = document.getElementById('my_button');
btn.addEventListener('click', function click(evt) {
console.log('clicked');
}, /*capturingPhase=*/false);function process(data) {
// do something
}
// 在这个块中定义内容就可以销毁了
{
var someObj = {};
process(someObj);
}
var btn = document.getElementById('my_button');
btn.addEventListener('click', function click(evt) {
console.log('clicked');
}, /*capturingPhase=*/false);2. let循环
for(let i = 0; i < 10; i++) {
console.log(i);
};
console.log(i); // ReferenceError{
let i;
for(i = 0; i < 10; i++) {
let j = i; // 每次迭代中重新绑定
console.log(j);
};
}var foo = true, baz = 10;
if (foo) {
var bar = 3;
if (baz > bar) {
console.log( baz );
}
// ...
}var foo = true, baz = 10;
if (foo) {
var bar = 3;
// ...
}
if (baz > bar) {
console.log( baz );
}var foo = true, baz = 10;
if (foo) {
let bar = 3;
if (baz > bar) { // <-- 移动代码时不要忘了 bar!
console.log( baz );
}
}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!函数时 JavaScript 中最常见的作用域单元。块作用域值的是变量和函数布局可以属于所处的作用域,也可以属于某个代码块(通常指 {...} 内部)ES3 开始, try/catch 结构在 catch 分句中具有块作用域。ES6 引入了 let,const 关键字来创建块级作用域。•问题标注 Q:(question)
•答案标注 R:(result)
•注意事项标准:A:(attention matters)
•详情描述标注:D:(detail info)
•总结标注:S:(summary)
•分析标注:Ana:(analysis)
•提示标注:T:(tips)