在es5的语法糖里,只有函数作用域和全局作用域。 所谓函数作用域就是:属于这个函数里的全部变量都可以在整个函数的范围内使用以及复用。举个例子:
function example(){
var k = 2
function insert() {
console.log(k, 'in example')
}
insert()
}
function anotherExample () {
console.log(k, 'in anotherExample')
}
example() //这里会打印 :2 "in example"
anotherExample() // 这一步浏览器引擎就会报错:Uncaught ReferenceError: k is not defined
复制代码
就像刚才我说的:函数里的变量只能在函数范围内使用,函数以外的范围是不可以访问到的。
变量a是在example函数里被定义的。 由于insert函数是在example函数作用范围内,所以它在打印变量k的时候,会先在它自己的函数作用域里找,然后找不到再往它的上一级作用域也就是example函数作用域里去找(这种查找方式也成为作用域链查找),找到了就打印了。
但是anotherExample函数在函数example作用范围之外,所以在执行函数的时候,先在自己的作用域找变量k,找不到之后它直接往他的上一级作用域也就是全局作用域上去找,也找不到,于是就会报错了。
函数作用域的作用
接下来我们可以谈一谈函数作用域的用处: 我们可以使用函数作用域把一些变量和函数“隐藏起来”。
为什么我们要这样做?
一是为了软件设计中提倡一种原则:应该最小限度地暴露出必要内容,而将其他内容都隐藏起来,将变量私有化。
二是为了规避冲突,避免同名标识符之间的冲突。 例如:
function foo () {
function bar (a) {
i = 3
console.log(i)
}
for (var i = 0; i < 10; i++ ){
bar (i * 2)
}
}
foo()
复制代码
我们运行一下以上代码就会发现,进入了无限循坏。 因为在走for循环的时候在里面调用了bar这个函数,bar这个函数又将同名变量i给赋值3,导致循环条件一直是被满足小于10的条件。
而且如果在我们的程序中使用第三方库,他们没有很好的将他们的内部变量或者函数私有化,那么可能会和我们自己命名的变量冲突。
所以我们可以是使用函数作用域来规避这些问题。 但是由于函数名本身就污染了他自己所在的作用域,而且要显式地去调用函数才能执行其中代码,所以一般我们都使用立即执行函数(既可以是匿名又可以在声明的结束之后立即调用)来做变量或者函数的私有化。
例如:
var k = 1;
(function () {
var k =2
console.log(k, 'iife') // 2, iife
}());
console.log(k, 'window') // 1,window
复制代码