在一些c语言中的编程中,花括号内的每一段代码都具有各自的作用域,而且变量声明他们的代码段之外是不可见的,我们称之块级作用域。而javascirpt中没有块级作用域。javascirpt取而代之地使用了函数作用域:变量在声明它们的函数体嵌套的任意函数体内都是要有定义的。
以下代码所示,在不同的位置定义了变量i,j和k,他们都在同一个作用域内–这三个变量正在函数体内均是由定义的
function test(){
var i = 0; //i 在整个函数体内均是要有定义的
if(typeof 0 == "object"){
var j = 0;
for(var k = 0; k < 10; k++){ //k在函数体没有定义的,不仅仅是在循环内
console.log(k); //输出数字0~9
}
console.log(k); //k已经定义了,输出10
}
console.log(j); //j已经定义了,但可能没有初始化
}
javascirpt的函数作用域是指在函数内声明的所有变量在函数体内始终均是可见的。有意思的是,这意味着变量在声明之前甚至已经可用。javascirpt的这个特性被非正式地称为声明提前(hoisting),即javascirpt函数里声明的所有变量(但不涉及赋值)都被称”提前”至函数体的顶部,看一下代码;
var scope = "global"
function f (){
console.log(scope) ; //输出'undefined',而不是'global'
var scope = 'local'; //变量在这里赋值,函数体内均有定义的
console.log(scope); //输出'local'
}
你可能会误认为第一行输出是‘gloal’,由于函数作用域的特性,局部变量在整个函数体始终是有定义的,也就是说函数体的局部变量掩盖了同名的全局变量。
在函数体内的变量声明 ‘提前’ 至函数体顶部,同时变量初始化留在原来的位置:
function f (){
var scope; //在函数顶部声明了全局变量
console.log(scope); //变量存在,但其值是'ubdefied'
scope = 'local'; //这里将其初始化赋值
console.log(scope) //这里它具有了我们所期望的值
}
在具有块级作用域的编程语言中,在狭小的作用域里让变量声明和使用变量的代码尽可能靠近彼此,通常来讲这是一个非常不错的编程习惯。由于javascirpt没有块级作用域,因此一些程序员特意将变量声明放在函数体的顶部,而不是将声明靠近放在使用变量之处,这种做法使得他们的源代码非常清晰地反应真实的变量作用域。