什么是作用域?
作用域就是一个变数的生存範围,一旦超出了这么範围就无法存取到这个变数。
在ES6之前,Javascript只拥有函数作用域和全域作用域,函式作用域也就是说每一个函数都有自己的作用域,在作用域外面无法存取到函数内定义的变数,全域作用域代表global任何地方都能存取到,而ES6引入的let与const,多了块状作用域的概念,
作用域式分层的,内层作用域可以取得外层作用域的变数,反之则不行。
全域作用域(global)
全域作用域式全局性的,也就是说在整个Javascript程式中到处都可以使用,所有window物件
都属于全域作用域中,全局作用域有个缺点,如果我们定义了很多变数、函示但却没有使用那么它们就全部都在全局作用域中,会污染全局命名空间, 容易引起命名冲突。
var global = 123;test = () => { console.log(global); //123};test();
值得注意的是,尚未定义且直接赋值的变数都会自动宣告为全局作用域
这点在写成式的时候会常常造成错误。
test = () => { var func = 123; global = 456; console.log(func); //123};test();console.log(func); //ReferenceError: func is not definedconsole.log(global); //456 虽然global在函示内被赋值,但却没有定义型态,所以被自动归类为全局作用域
函式作用域
函式作用域只有在函数内可以被访问到,所以在函数内所宣告的变数不会洩漏到全域中造成全域汙染,而函式作用域可以访问父层作用域的变数,但自身作用域的变数层级较高。
test = () => { var a = 10;};console.log(a); //ReferenceError: a is not defined
var global = 123;test = () => { var global = 456; console.log(global); //456}test();
块级作用域
在ES6中引入了let与const两个变量宣告,新增了块级作用域的概念。
为什么需要块级作用域?
由于ES5只有全局作用域与函数作用域,所以常常会造成一些想像不到的错误。
var tmp = new Date();test = () => { console.log(tmp); if(false){ var tmp = "hello world"; }};test(); //undefined
在上面的程式中,虽然function可以读取到全域的tmp变数,但是由于变量提升(hoisting)的关係导致输出为undefined。
var tmp = new Date();test = () => { var tmp; //hoisting console.log(tmp); //undefined if(false){ tmp = "hello world"; }};test(); //undefined
而ES6新增了块级作用域搭配上let,const不会hoisting的情况,可以避免常数的不确定性。
test = () => { let n = 5; if(true){ let n = 10; } //由于let n=5与console属于同级块状作用域,而let n=10属于下一层级的块状作用域,所以不会互相影响 console.log(n); //5 };
还有值得注意的地方,ES6的块级作用域必须有大括号
,若没有JavaScript则认为不存在块级作用域。
//Errorif (true) let x = 1;if (true) { let x = 1;}
参考资料 :
ECMAScript 6 入门
深入理解JavaScript作用域和作用域链
所有的函式都是闭包:谈 JS 中的作用域与 Closure