《你不知道的javascript》一、函数作用域和块作用域
函数中的作用域
所谓函数作用域,就是属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
1 function foo(a) { 2 var b=a; 3 function bar(c){ 4 var c=b*2; 5 console.log(c); 6 } 7 bar(); //4 8 } 9 foo(3);
1 function foo(a) { 2 var b=a; 3 function bar(c){ 4 var c=b*2; 5 console.log(c); 6 } 7 bar(); 8 } 9 bar(2); //not defined
命名冲突
在同一作用域中,相同的命名会引起冲突。
1 function foo() { 2 function bar(a){ 3 i=3; 4 console.log(i+a); 5 } 6 for(var i=0;i<10;i++){ 7 bar(i); 8 } 9 } 10 foo();
上面的代码将会引起冲突,函数会一直执行下去,行成死循环。
如何避免命名冲突呢?
(1)全局命名空间
一些第三方库通常都是在全局对象中声明一个独特的对象,库的方法和实例都在这个对象中,当我们调用时直接调用这个独特的对象就好了。
1 var MyLibrary={ 2 param1:'param1', 3 doSomething:function () { 4 5 }, 6 doElseThing:function () { 7 8 } 9 //....... 10 }
(2)模块管理
另一种避免冲突的方法和现在的模块机制很接近,就是从众多模块管理器中挑选一个来使用。使用这些工具,任何库都无需将标识符加入到全局作用域中,而是通过依赖管理器的机制将库的标识符显示地导入到另一个特定的作用域中
立执行函数
将函数包裹在一对括号中,使之成为一个表达式,再在后面加一个括号,使之执行,这样函数就会自执行。这种模式又称IIFE。
自执行函数有两种写法形式,第一种是将整个函数写在括号中,再在括号外面加一对括号:(function(){//do something})();
还有一种是:(function(){//do something}())。
这两种写法功能都相同。
自执行函数传参
1 (function(doc){ 2 var a='按钮'; 3 doc.getElementById('btn').innerText=a; 4 })(document);
块作用域
当我们写for循环时,我们都希望i只在循环的上下文中起到作用,而事实上i会被绑定到他所在的作用域中。比如上面的命名冲突中的例子。
那么如何解决块作用域的问题呢?
(1)使用自执行函数将循环包住,设置私有作用域。
1 function foo(a) { 2 function bar(a){ 3 i=3; 4 console.log(a+i); 5 } 6 (function(){ 7 for(var i=0;i<10;i++){ 8 bar(i); 9 } 10 })(); 11 } 12 foo(3); //3 4 5 6 7 8 9 10 11 12
(2)使用es6中的let
let关键字为其声明的变量隐式地劫持了所在的作用域,上述例子可以改成:
1 function foo(a) { 2 function bar(a){ 3 i=3; 4 console.log(a+i); 5 } 6 for(let i=0;i<10;i++){ 7 bar(i); 8 } 9 } 10 foo(3); //3 4 5 6 7 8 9 10 11 12
(3)es6中const也会创建块级作用域。
const可以创建块级作用域,但是他表示常量。修改const创建的变量的值会报错。
1 var foo=true; 2 3 if(foo){ 4 var a=2; 5 const b=3; 6 7 a=3;//正常 8 b=4;//错误 9 } 10 11 console.log(a); //3 12 console.log(b); //b is not defined
态度决定一切,改变 从关注开始...