1.没有块级作用域
Javascript没有块级作用域这个特点被《JavaScript语言精粹》列为js几大毒瘤之一。
正是因为没有块级作用域,所以在代码块(括在一对花括号中的一组语句)中声明的变量在包含此代码块的函数的任何位置都是可见的。
function fn(o){
if(o){
var j=10;
}
console.log(j);
}
fn(1); //可以打印出10
在if语句里定义了变量j。如果在C、C++、Java中,j会在执行完if语句完毕后就被销毁,但是在js里,if语句的变量声明会将变量添加到当前的执行环境。
尤其是使用for语句时候要更加牢记这一点,
for(var i=0;i<10;i++){
//dosomething(i);
}
console.log(i); //10
对于for语句里面的i值是很容易犯错的。
举个在Js作用域与作用域链详解--Skycrab的专栏的小例子,
<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.οnlοad=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
但是作者在文章里并没有给出解决方案,其实,对于这样的问题,
等到click各个button时,就是i在循环结束之后,i就等于4了,所以弹出的都是Button4,解决这样的问题就可以用this,用this.index绑定各个button的i值。
<html>
<head>
<script type="text/javascript">
function buttonInit(){
var b=document.getElementsByTagName("button");
for(var i=0;i<3;i++){
b[i].index=i;
b[i].addEventListener("click",function(){
alert("Button"+(this.index+1));
},false);
}
}
window.οnlοad=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
这样就可以实现点击按钮弹出第几个按钮的效果了。偶然间看到了高程设计书上的例子,感觉用IIFE(immediately-invoked function expression,立即执行函数)会更好一点。
把buttonInit函数改成这样就好了。
function buttonInit(){
var b=document.getElementsByTagName("button");
for(var i=0;i<3;i++){
(function(i){
b[i].οnclick=function(){
alert("Button"+(i+1));
}
})(i);
}
}
这样看起来代码更加清爽,而且也不用再引入新的参数了。
2.作用域链
每一个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都得保存在这个对象里,只有在解析器处理数据的时候才会在后台使用它,
在web浏览器里,全局执行环境就是window对象,每个函数都有自己的执行环境。
当代码在一个环境中执行的时候会创建变量对象的一个作用域链。以便可以保证对执行环境有权访问的所有变量和函数的有序访问。
全局执行环境的变量对象始终都是作用域链中的最后一个对象。
var color="blue";
function changeColor(){
if(color==="blue"){
color="red";
}else{
color="blue";
}
}
changeColor();
console.log("Color is now "+color); //red
函数changeColor()的作用域链包含两个对象,它自己的变量对象(arguments对象)和全局的变量对象。
这个例子说明了函数内部可以访问到全局变量。
var color="blue";
function changeColor(){
var anothercolor="red";
function swapColors(){
var tempColor=anothercolor;
anothercolor=color;
color=tempColor;
//这里可以访问color和anothercolor和tempcolor
}
//这里可以访问color和anothercolor,但不能访问tempcolor
}
//这里只能访问color
changeColor();
上面的例子一共有3个执行环境:全局环境、changeColor()的局部环境和swapColors()的局部环境。
全局环境中有一个变量color和一个函数changeColor()。
changeColor()的局部环境中一个名为anotherColor的变量和一个swapColors()的函数,但它可以访问全局环境中的变量color。
swapColors()的局部环境中有一个变量tempColor,该变量只能在这个环境中访问到,无论全局环境还是changeColor()环境中都无权访问tempColor。
在外部环境访问访问不到的局部变量的话,不会Undefined,而是会Uncaught ReferenceError: *** is not defined.