JavaScript拾漏之变量作用域
变量作用域
说到变量作用域,一般会有以下的说法:
在所有函数之外声明的变量,叫做全局变量,因为它可被当前文档中的其他代码所访问,其作用域称为全局作用域。在函数内部声明的变量,叫做局部变量,因为它只能在该函数内部访问,其作用域称为局部作用域(函数作用域、块作用域)。
但这样的说法是有问题的,不信试试下面的代码。
var fo1 ="JavaScript";
function foo1(){
console.log(fo1);
fo1 = "scope";
console.log(fo1);
}
foo1();
console.log(fo1);
// JavaScript scope scope
var fo2 ="JavaScript";
function foo2(){
console.log(fo2);
var fo2 = "scope";
console.log(fo2);
}
foo2();
console.log(fo2);
// undefined scope JavaScript
上面两段代码答案是什么?是不是和自己想的不一样?
下面对于变量的作用域知识做一些整理,也是对上面问题的一个解答。能力有限,有出入处,希望可以留言给予斧正。
变量的声明
JavaScript中三种声明
var和let的区别
- var声明变量要么是全局的,要么是函数级的,而无法是块级的;let把变量的作用域限制在块级域中
//var
function varTest() {
var fo1 = 31;
if (true) {
var fo1 = 71; // same variable!
console.log(fo1); // 71
}
console.log(fo1); // 71
}
//let
function letTest() {
let fo2 = 31;
if (true) {
let fo2 = 71; // different variable
console.log(fo2); // 71
}
console.log(fo2); // 31
}
- let的作用域是块,而var的作用域是函数
var fo1 = 5;
var fo2 = 10;
if (fo1 === 5) {
let fo1 = 4; // The scope is inside the if-block
var fo2 = 1; // The scope is inside the function
console.log(fo1); // 4
console.log(fo2); // 1
}
console.log(fo1); // 5
console.log(fo2); // 1
可以用let 来代替var ,在for定义块中使用块级变量.
for (let i = 0; i < 10; i++) {
console.log(i); // 0, 1, 2, 3, 4 ... 9
}
console.log(i); // i is not defined
声明全局作用域
- 显示声明
在函数体(function)外声明
var fo;
fo = "global variable"
function foo() {
alert(fo);
}
- 隐式声明
隐式声明时,变量默认声明为全局作用域
// 函数外隐式声明
fo2 = "global variable"
function foo2() {
console.log(fo2); // "global variable"
}
// 函数体内隐式声明
function foo3(){
fo3 = "global variable"
}
// 函数内的声明要有效,则需要先调用函数,否则会出现ReferenceError: fo3 is not defined
console.log(fo3); // ReferenceError
// 调用函数
foo3();
// 在函数调用后,隐式声明的fo3变量默认声明为全局作用域
console.log(fo3); //"global variable"
- window声明全局变量
每个 JavaScript 环境有一个全局对象,当你在任意的函数外面使用 this 的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属 性。在浏览器中,方便起见,该全局对象有个附加属性叫做 window,此 window(通常)指向该全局对象本身。
myglobal = "hello"; // 不推荐写法
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"
这种方式经常被用到一个匿名函数执行后将一些函数公开到全局。 如JQuery中:
window.jQuery = window.$ = jQuery;
声明局部作用域
JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。
// 全局变量
var fo = 1;
var bar = 2;
var i = 2;
function foo(i) {
// 函数 foo 内的局部作用域
i = 5;
var fo = 3;
bar = i + 4;
}
foo(10);
fo 和 i 是函数 foo 内的局部变量,而此时的bar是隐式声明,赋值将会覆盖全局作用域内的同名变量。
此时的i为局部变量,而局部变量在函数内优先级高于全局变量,所以此时函数内的i为5。而bar是隐式声明,经过foo(10)函数的调用后,会导致函数内的bar转为全局变量,会覆盖函数外声明的全局变量值。而fo由于函数内是显示声明,所以函数内的fo不会对函数外的fo影响。
声明全局和局部作用域的区别
声明变量(Declared Variable)和非声明变量(Undeclared Variable)的区别是:
- 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的
function foo() {
fo1 = 1; // 在严格模式(strict mode)下会抛出ReferenceError异常。
var fo2 = 2;
}
foo();
console.log(fo1); // "1" 。
console.log(fo2); // ReferenceError: fo2 is not defined; fo2未在foo内部声明
- 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建
console.log(fo); // ReferenceError: fo is not defined。
console.log('still going...'); // 永不执行。
var fo;
console.log(fo); // "undefined"或""(不同浏览器实现不同)。
console.log('still going...'); // "still going..."
- 声明变量是它所在上下文环境的不可配置属性(non-configurable property),非声明变量是可配置的(例如非声明变量可以被删除)。即,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性
var fo1 = 1;
fo2 = 2;
// 在严格模式(strict mode)下抛出TypeError,其他情况下执行失败并无任何提示。
delete this.fo1; // 非严格模式下,false
delete this.fo2; // true
console.log(fo1, fo2); // 抛出ReferenceError。
// 'fo2'属性已经被删除
var创建的全局变量(任何函数之外的程序中创建)是不能被删除的
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的
// 定义三个全局变量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {
global_fromfunc = 3; // 反面教材
}());
// 试图删除
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true
// 测试该删除
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"
由于以上三个区别,声明变量的错误会导致不可预测的结果。于是,无论是否在函数中,我们总是推荐使用声明变量。在ECMAScript 5的严格模式(strict mode)下,给一个未声明的变量赋值会抛出错误。
总结
- JavaScript没有块作用域
- 最外层函数和在最外层函数外面定义的变量拥有全局作用域
- 给一个非声明变量赋值会隐式创建一个全局变量(全局object的一个属性),即是说所有末定义直接赋值(隐式声明)的变量自动声明为拥有全局作用域
- 所有window对象的属性拥有全局作用域
- JavaScript 通过函数管理作用域。在函数内部声明的变量只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。
参考内容
深入理解JS中的变量作用域
Javascript:谈谈JS的全局变量跟局部变量
JavaScript声明全局变量三种方式的异同