JavaScript引擎在执行时,会把所有变量的声明都提升到当前作用域的最前面。
比如下边的代码:
var v = "hello";
(function(){
console.log(v);
var v = "world";
})();
这段代码的执行结果为undefined
这说明两个问题:
1.匿名function作用域中的v没有受到外部的影响,这说明javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构。
2.function作用域内,变量v的声明被提升了,等同如下:
var v = "hello";
(function(){
var v; //
console.log(v);
v = "world";
})();
声明提升
当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明
(function(){
var a = "1";
var f = function(){};
var b = "2";
var c = "3";
})();
变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:
(function(){
var a,f,b,c;
a = "1";
f = function(){};
b = "2";
c = "3";
})();
请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:
(function(){
//var f1,function f2(){}; //被隐式提升的声明
f1(); //这种写法会报错,无法调到f1函数,必须写在函数表达式之后
f2();
var f1 = function(){};
function f2(){}
})();
上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。
未用var声明的变量作用域
function t(flag){
if(flag){
s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
}
t(true);
console.log(s);
s会输出为:2 ifscope
这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。
作用域链
先来看一段代码:
name="lwy";
function t(){
var name="tlwy";
function s(){
var name="slwy";
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();
当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是"slwy"。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy"
下面看一个很容易犯错的例子:
<html>
<head>
<script type="text/javascript">
function buttonInit(){
for(var i=1;i<4;i++){
var b=document.getElementById("button"+i);
b.addEventListener("click",function(){ alert("Button"+i);},false);
}
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢?
很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?
当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,所以弹出”button4“。
with语句
说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。