什么是作用域?
任何编程语言都有作用域的概念,简单来说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
JavaScript的作用域是靠函数来形成的,也就是说一个函数的变量在函数外不可以访问。
全局作用域
任何地方都能访问到的对象拥有全局作用域。
- 函数外面定义的变量拥有全局作用域
1 2 3 4 5 6 7 8 9
| var n = 2; function fn(){ var a = 1; return a; }
console.log(fn()); // 1 console.log(n); // 2 console.log(a); //报错 a is not defined
|
- 未定义直接赋值的变量自动声明为拥有全局作用域
1 2 3 4 5 6 7 8 9
| var n = 2; function fn(){ a = 1; return a; }
console.log(fn()); // 1 console.log(n); // 2 console.log(a); // 1
|
- window对象的属性拥有全局作用
局部作用域
局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所以在一些地方会把这种作用域成为函数作用域。
1 2 3 4 5 6 7 8 9 10 11
| var n = 2; function fn(){ var a = 1; return a; }
console.log(fn()); // 1 console.log(n); // 2 console.log(a); //报错 a is not defined
// a是函数内部声明并赋值,拥有局部作用域,只能带函数fn内部使用,在fn外部使用就会报错,这就是局部作用域的特性,外部无法访问。
|
作用域链
通俗的讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。
当执行函数时,总是先从函数内部找寻局部变量。
如果内部找不到(函数的局部作用域没有),则会向创建函数的作用域(声明函数的作用域)寻找,一次向上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var a = 1;
function fn(){ var a = 10; function fn1(){ var a = 20; console.log(a); // 20 } function fn2(){ console.log(a); // 10 }
fn1(); fn2(); }
fn(); console.log(a) // 1
|
闭包
提到作用域就不得不提到闭包,简单来讲,闭包外部函数能够读取内部函数的变量。
优点:闭包可以形成独立的空间,永久的保存局部变量。
缺点:保存中间值的状态缺点是容易造成内存泄漏,因为闭包中的局部变量永远不会被回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function f1() { var n = 999; nAdd = function () { n += 1; } function f2() { console.log(n); } return f2; } var result = f1();
result(); // 999 nAdd(); // 执行 n += 1 result(); // 1000
|
ES6的块级作用域
ES6引入了块级作用域,明确允许在块级作用域中声明函数,let和const命令都涉及块级作用域。块级作用域允许声明函数只在使用大括号的情况下成立,如果未使用大括号,会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| // ES5 中作用域 var callbacks = [] for (var i = 0; i <= 2; i++) { callbacks[i] = function() { return i * 2 } }
console.table([ callbacks[0](), callbacks[1](), callbacks[2](), ])
;((function() { const foo = function() { return 1 } console.log("foo()===1", foo() === 1) ;((function() { const foo = function() { return 2 } console.log("foo()===2", foo() === 2) })()) })())
// ES6中块级作用域 const callbacks2 = [] for (let j = 0; j <= 2; j++) { callbacks2[j] = function() { return j * 2 } }
console.table([ callbacks2[0](), callbacks2[1](), callbacks2[2](), ])
{ function foo() { return 1 }
console.log("foo()===1", foo() === 1) { function foo() { return 2 }
console.log("foo()===2", foo() === 2) } console.log("foo()===1", foo() === 1) }
|
任何值得做的事就值得把它做好。- Whatever is worth doing is worth doing well.