作用域(scope)类型
- 全局变量,在函数外定义的变量
- 局部变量,函数体内定义的变量,还有函数参数
同名处理
函数体内,局部变量的优先级高于同名的全局变量,但局部变量声明时必须用var,否则会直接引用全局变量。
var num =10 //全局变量
var name = 'Mike' //全局变量
function sum(x, y) //x y 是局部变量仅在{}内起作用
{
var num //num也是局部变量
num = x + y
name = 'Smith' //修改了全局变量name
str = 'hello' //不带var,声明的是全局变量
return num
}
name // 'Mike' sum还没执行,不会修改name
sum(1, 2) //3
num //10
name // 'Smith' sum执行后修改了name
str // 'hello' str是函数体外可访问的全局变量
x //ReferenceError ,函数体外不能访问,报错
嵌套作用域
嵌套函数体内声明的变量遮蔽外层函数同名变量
var name = 'Mike'
function fun1(){
var name = 'Smith'
function fun2(){
var name = 'Jack'
return name
}
return fun2()
}
fun1() // Jack
函数作用域
Javascript中没有类似C的块级作用域,而是使用了函数作用域(function scope):变量在声明它们的函数体和此函数嵌套的任意函数体内都是有定义的。下面很好的展示了块级作用域和函数作用域的不同(注意j和k,在C语言中在if{}外是没有意义的):
function test(o){
var i = 0 // i在函数体内是有意义的
if(typeof(o) == 'object'){
var j = 0 // j在整个test函数体内有意义,不仅仅在if段内
for(var k=0;k<10;k++){ //k在整个test函数体内有意义,不仅仅是在for段内
console.log(k)
}
console.log(k)
}
console.log(j) //引用不会报错,但值不确定,如果o不是对象,j就是undefined
console.log(k) //通j
}
声明提前
函数体内的变量声明会被提前至函数顶部,而初始化会留在原来的位置。
var name = 'Mike'
function test(){
console.log(name) //实际输出undefined,而不是Mike
var name = 'Smith'
console.log(name)
}
test()
以上代码等同于
var name = 'Mike'
function test(){
var name //声明被提前
console.log(name)
name = 'Smith' //初始化留在原位
console.log(name)
}
test()
由于没有块级作用域和声明提前的特性,一个好的习惯是,把函数体内用到的变量都在函数前端声明,使作用域更清晰。
作为属性的变量
当声明了全局变量时,实际上是定义了全局对象的一个属性。
- 使用
var
声明的全局变量,不可以通过delete删除 - 不使用
var
声明的全局变量(直接给未声明的变量赋值),可以通过delete删除
Javascript可以使用this引用全局变量,但通过任何方法都不能在函数外引用函数内var定义的变量。
作用域链
每一段Javascript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。
作用域链是一个对象列表或链表,这个链表定义了变量名被查找的顺序。全局作用域链表只有一个元素,指向所有的全局变量;函数的作用域链表的第一个元素指向了本级作用域定义的所有变量,下一个元素指向上层作用域(嵌套当前函数的函数或全局代码)的所有变量,再下一个元素又是更上层作用域的所有变量,直至全局作用域所有变量为止。
作用域链是js引擎查找变量的一种方式,我们通过阅读代码就可以将作用域链完整的画出。
网友评论