<html>
<head>
<title>变量作用域</title>
<script type="text/javascript">
/*
变量的作用域分为普通变量和引用变量
对于普通变量,变量里只是保持着某个具体
的值,对于引用变量,引用里只是保存对某
一个变量的引用,或者只是保存内存中某个
区间的地址,对于普通变量,当我们对其值进行
复制的时候,两个变量完全是独立的,各自操作
互不影响,但是,对于引用变量,进行复制后,两个
变量都是引用的同一个对象,因此在操作的时候,会
对另外的复制引用的变量的值产生影响,如下:
*/
//对于普通的变量
function print(str){
document.write(str+"<hr>");
}
var var_1 = 'felayman';
var var_2 = var_1;
print('var_1 = '+var_1+',var_2 = '+var_2);//var_1 = felayman,var_2 = felayman
//当我们对其中一个进行修改,另一变量值是不会受到影响的
var_2 = 'felayman1';
print('var_1 = '+var_1+',var_2 = '+var_2);//var_1 = felayman,var_2 = felayman1
//但是对于引用变量,则在复制后会产生影响
var obj1 = new Object();
obj1.name = 'felayman';
print('obj1.name = '+obj1.name);//obj1.name = felayman
var obj2 = obj1;
obj2.name = 'felay';
print('obj1.name = '+obj1.name+'obj2.name = '+obj2.name);//obj1.name = felayobj2.name = felay
/*
对于参数的传递
只有一个规则,就是按照值传递,与变量的访问不同,ecmascript不存在象其他语言一样的参数可以引用
*/
var name = 'felayman';
print(name);//全局变量 打印为felayman
function transport(name){
name = '小熊猫';//虽然改变了参数的内容,但是name的定义初始化值并没有发生改变依然是felayman
print('参数:'+name);
}
transport(name);//小熊猫
print(name);//felayman,说明,在函数内部是无法修改引用变量所指向的变量的,但是可以通过返回值的方式,获取到我们象修改的值
function return_var(name){
name = '小熊猫';
return name;//虽然没有修改name所指向的值,但是,获取到我们想要的name修改后的值
}
name = return_var(name);
print(name);//小熊猫
//下面对对象进行测试
function setName(person){
print(typeof person);//其实这里,我也比较困惑,为什么js引擎为什么会在这里判断出,person的类型是object
person.name = 'felayman';
person = new Object();
person.name = 'felaytest';
print(person === person);
}
var person = new Object();
setName(person);
print(person.name);//这里依然是felayman
print(person instanceof Object);
/*
很多人比较困惑,不是在setName函数中将传入的参数的name值变为felaytest么,为什么还会输出felayman呢?
不过也说明一个问题,那就是ecmascript的参数不存在引用传递,因为引用变量person指向的内容并没有发生变化
或者这么说,当参数按照值来传递的话,即在person被初始化的时候,它指向的是是在全局变量中定义的对象,而且
这个person一旦指向该对象,指向就步不会上变化,及时在函数中,函数的参数被实例化,但是person指向是不变的
因此,它的属性也不会发生变化.如果你曾经编写过OOP语言,则不要纠结于为什么会这样,这只是ecmascript的创造
者为了突出与其他语言的区别吧
*/
/*
ecmascript的执行环境和作用域
有时候去细心研究ecmascript的执行环境和作用域是一件特别振奋和受挫的一件事,我们经常会在它的作用域的特殊性中
找不到出口,有时候当你发现一些玄机的时候,又会觉得这么语言相比于其他的语言,的确尤其出色的地方,ecmascript的作
域与其他语言有很大的不同,其他语言的作用域都是以块为单位的,但是ecmascript的作用域却是一个链,每个链都会有一个
对象,而这个对象就保存着它对应链的所有定义的变量和函数。,虽然我们无法访问这个对象,但是js解析器会在后台去访问这些
对象来获取每个变量所属的作用域。例如下面的例子
*/
color = 'blue';//属于全部变量
var color = 'red';//属于全部变量
var size = 100;
print(color);//red
function getColor(){
var color = 'yellow';//属于局部变量
print(size);//undefined
var size = 1000;
print(color);
}
getColor();
/*
上面的函数中的size为什么会出现undefined呢?因为,js引擎在查找一个变量的时候,首先回去查找定义的地方,如果找到,则将变量的作用域
锁定在该链中,因此,size的作用域就在函数内部,但是执行到print(size);的时候,size还没有初始化,因此会出现未定义,因此也可以看出
js代码的查找优先原则,即ecmascript在引入一个函数的时候首先要做的是将函数内的所有变量的作用域确定下来,而非执行的时候才去确定
变量的作用域,当前这也用于所有的变量和函数。不过还有一点非常重要,就是ec,ascript中没有块级作用域,如下:
*/
var bool = true;
if(true){
if(true){
var name = 'felaman';
}
}
print(name);//felayman
print(window.name);//felayman
/*
上述代码依然能打印出name的值,尽管我使用了两个{}但是都没有锁住name的范围,name依然属于window作用域的变量,如果这个不明显的话
那么下面这个例子,则会更好的说明这个例子
*/
for(var i=0;i<10;i++){
name = 'test';
document.write(i+"\t");
}
print(i);
print(window.i);
/*
这里会得到什么呢?如果是其他语言的话,会报错的,但是在js中,这个i是全局变量,真的很意外,因此很多熟悉js的人,在定义循环
的时候,往往会吧循环定义在函数内部,用闭包的形式来避免循环变量的冲突,如下
*/
function for_statement(size){
for(var j=0;j<size;j++){
name = 'test';
document.write(j+"\t");
}
document.write('<br/>');
}
for_statement(10);
//print(100);
print(j);
print(j+'?');
/*
也许上述会有人认为会打印出undefined,其实我也是这么想的,不过很遗憾,什么都不会出现,为什么呢?这由于是我的错误,因为我在
定义print()函数的时候,接受了一个参数,可惜,这里我传入的参数j,定义的函数无法获取到j的值和类型,因此,当浏览器解析到该语句
document.write()的时候,就会出现错误,但是浏览器为了不影响用户体验,往往会隐蔽这种错误,只有我们开发人员进行调试的时候才会
去发现错误地方。谷歌浏览器会出现这样的错误:Uncaught ReferenceError: j is not defined,如果是IE的话,会出现这样的错误:
SCRIPT5009: “j”未定义,而firefox呢?ReferenceError: j is not defined。可看出主流浏览器对该类型的错误提示差不多。
IE9一下的浏览器会直接在页面上显示出现脚本错误的。
*/
</script>
</head>
</html>