javascript 值传递与作用域
ECMAScript的变量是松散类型的,在将一个值赋给变量时,解析器必须确定这个值是基本类型还是引用类型。基本类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在堆内存中的对象,JavaScript 不容许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是
操作实际的对象。因此 JavaScript 引用类型是按引用访问的。
对于引用类型我们可以为他添加属性和方法,但是我们不能给基本类型的值添加属性,虽然这样做不会导致任何的错误。如下所示:
var num = 1;
num.age = 12; //这里隐式调用了Number()将其转为Number对象,然后为其赋值,所以这里不会报错。执行完之后解除了该对象。
alert(num.age); //undefined
@复制变量
除了保存的方式不同之外,在一个变量向另一个变量复制基本类型和引用类型时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值
复制到为新变量分配的位置上。如下所示:
var num = 12;
var age = num;
age = 24;
alert(num); //12
alert(age); //24
上面的例子中num变量保存的是12 ,然后将num 复制给新变量 age,该值是num的一个副本。 然后修改了age 的值,最后打印这2个值,修改age 并没有改变num的值。
当从一个变量想另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的内存空间中。不同的是,这个值的副本实际上是一个指针,而这个指针
指向存储在堆内存中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象。因此改变其中一个变量就会影响到另一个变量。 如下所示:
var po = {
age : 24,
name : "tony"
}
var ob = po;
ob.name = "miracle";
alert(po.name); //miracle
alert(ob.name); //miracle
如同上述代码所示,修改了复制后的变量ob的name 属性会影响 原来的po对象的name属性,因为这两个变量引用的是同一个对象,而他们的值都是指向该对象的指针。综上所述:
*引用类型复制,其实是复制了指针。
@参数传递
ECMAScript 中所有的函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制给另一个变量一样。基本类型值的传递如同基本类型
变量的复制一样,而引用类型值的传递,则如同引用类型的变量复制一样。在向参数传递基本类型值的时候,被传递的值会被复制给一个局部变量。在向参数传递引用类型时,会吧这个值在内存
中的地址复制给一个局部变量,因此这个局部变量的变化会反映到函数的外部。如下所示:
function basicFun(num){
num += 12;
return num;
}
var sum = 12;
var age = basicFun(sum);
alert(sum); //12
alert(age); //24
如上列代码所示,先定义了一个sum 其值为12, 然后调用basicFun()将sum复制给参数num,然后在basicFun()函数中参数 num 进行了+12操作,但这个操作不会影响
函数的外部变量sum。参数num与变量sum互不相识,他们只是具有相同值而已。使用基本类型比较简单,如果是使用引用类型就可能有些复杂了。请看下列代码。
function quoteFn(obj){
obj.name = "tony";
obj.age = 24;
obj = new Object();
obj.sex = "Men";
}
var person = {
name : "miracle",
age : 12
}
quoteFn(person);
alert(person.name); //tony
alert(person.age); //24
alert(person.sex); //undefined;
上列代码中创建了一个person对象,该对象的name 属性值为 "miracle",age 属性值为12。然后,这个变量被传递到quoteFn()中复制给参数obj,
在这个函数内部参数obj和变量person引用的是同一个对象。也就是说,即使这个变量是按值传递的,obj也会按引用来访问同一个对象,如是obj修改了name和
age属性的值之后,外部的person 也将有相同的反应。因为引用类型复制的是一个指向了对象的指针。而在quoteFn()函数中的第3行代码为参数obj重新创建
了一个对象,并为其添加了属性sex。如果person是按引用传递的,那么person就会制动修改为指向新对象。但是接下来访问新属性sex时,显示的是undefined;
这说明即使在函数的内部修改了参数的值,但原始的引用任然保持未变。实际上,在函数中重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行
完毕之后被立即销毁。
@检测类型
要检测一个变量是不是基本类型使用 typeof 操作符,而检测对象使用 instanceof 操作符, typeof 操作符返回该操作类型的字符串表示,
instanceof 操作符返回布尔值,如下所示;
alert(typeof true); //boolean
alert([] instanceof Array); //true
@执行环境和作用域
JavaScript 中没有块级作用域,他有全局作用域和函数作用域(局部作用域),全局执行环境是最外围的执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。
在WEB浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是window对象的属性和方法。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和
函数定义也随之销毁(全局执行环境知道应用退出--列如关闭网页或者浏览器)。
关于作用域链就是内部作用域可以访问外部作用域。