作用域
规定变量能够被访问的范围,离开这个范围的变量不能被访问
作用域就是一个独立的地盘,让变量不会外泄、暴露出去,也就是说作用域最大的用处就是隔离变量,不同作用域下同名的变量不会有冲突
作用域分为
-
全局作用域
-
局部作用域
-
函数作用域
-
块作用域
-
全局作用域
全局中声明的变量,任何地方都可以被访问
-
第一种在最外层函数外面定义的变量拥有全局作用域
var a = '我是全局的' // 全局变量
function fn() {
console.log(a); // 我是全局的
}
fn()
-
在函数内未定义直接赋值的变量自动声明为全局作用域
function fn() {
a = '未定义直接赋值' //全局变量
var b = '内层变量'
}
fn()
console.log(a); // 未定义直接赋值
console.log(b); // b is not defined
-
所有window对象的属性拥有全局作用域
全局作用域有个弊端:如果我们写了很多行的JS代码,变量定义都没有用函数包括,那么它们就全部都在全局作用域中,这样就会造成变量的污染,容易引起命名的冲突
因此
-
为 window对象动态添加的属性默认也是全局的(var) 不推荐
-
函数中未使用任何关键字声明的变量为全局变量 不推荐
-
尽可能少的声明全局变量,防止全局变量被污染
函数作用域
在函数内 部声明的变量只能在函数内容部被访问,外部无法直接访问
-
函数内部被访问,外部无法直接访问
-
函数的参数也是函数内部的局部变量
-
函数执行完毕后,函数内部的变量实际就被清空了
function fn(a,b) {
// 函数内定义的变量,外部无法访问
// 函数的参数也是局部变量
var s = a + b
console.log(s); // 18
}
fn(10,8)
console.log(s); // s is not defined
console.log(a); //a is not defined
块作用域(let,const)
ES6带来的新特性,在代码块中声明的语句或变量只在当前语句块中起作用
函数作用域和块级作用域没有直接的关系,函数作用域在ES5和ES6作用完全一样,变量不论是使用var,let,const声明,在外部都是不可以访问的
而块级作用域(let,const),指的就是用{}包裹的代码称为代码块。并且代码块内部用let和const声明的变量外部无法被访问
let flag = true
if (flag) {
let str = 'hello word'
console.log(str) //hello word
}
console.log(str) //str is not defined
经典案例
for (var i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i);
}, 200);
}
//4 4 4 4
使用let形成块级作用域
for (let i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i);
}, 200);
}
//0 1 2 3
作用域链
作用域链的本质 是底层的变量查找机制,函数被执行时,会优先查找当前函数作用域中的变量,如果找不到则会逐级查找父级作用域直到全局作用域,全局找不到 变量返回undefined,函数调用报错
let a = 20
function fn() {
var b = a + 10 // 当前作用域中没有a,所以往上找,a=20 ,所以b=20+10 = 30
function fn1() {
var c = 10
return b + c //当前作用域中没有b 往上找 b=30 30+10 最终返回40
}
// 由于当前作用域中没有d,一直往上找,最终也没有找到d,所以报错 d isnot defined
console.log(d);
return fn1()
}
fn() // 40
子作用域能够访问父作用域中的变量,而父作用域无法访问子作用域中的变量