js变量、作用域和内存
一、变量
1.分类:
变量可以分为基本类型值和引用类型值,像在之前的随笔中介绍的数值,字符串类型这些就是属于基本类型值。而引用类型值则是指对象。然而,引用类型的值是保存在内存中的对象,js和其他编程语言不同的是,我们不能够直接操作内存中的对象,只能通过当中的引用来操作。所以引用类型的值是按引用访问的。
2.保存方式:
基本类型值和引用类型值的保存方式是不一样的,举个例子:
var person=new object(); person.name="Nicholas"; person.age=27; alert(person.age);//"Nicholas"
以上例子创建了一个对象,并给该对象添加了一个name属性且赋值Nicholas,这个属性只要对象一直存在就也一样一直存在
var name="Nicholas"; name.age=27; alert(name.age);//undefined
而这个例子则是给基本变量添加了一个age属性,虽然js不会报错,但是该属性是不存在的
3.复制变量值
(1)对于基本类型值,一个变量向另一个变量赋值的时候,会在变量对象上创建一个新值,然后把该值复制到新变量分配的位置上,所以,新变量与原先变量两者是完全独立的。
(2)对于引用类型值,一个变量向另一个变量复制的时候,则是将指针复制过去,因此,两个变量实际上将引用同一个对象,改变其中一个对象,就会影响另一个对象。
4.传递参数
js中参数都是按值来传递的,也就是说,被传递的值如果是基本类型的值,该值将会被复制给一个局部变量。而在传递引用类型的值的时候,会把这个值在内存中的地址传递给一个变量,所以这个局部变量的变化会反映在函数的外部。例如:
//基本变量
function addTen(num){ num+=10; return num; } var count =20; var result=addTen(count); alert(count);//20,没有变化 alert(result);//30
//对象
function setName(obj){
obj.name="Nicholas";
}
var person=new object();
setName(person);
alert(person.name);//"Nicholas"
*因为在局部作用域中修改的对象会在全局作用域中反映出来,所以很多开发人员认为对象是按引用传递的。为了证明是按值传递,我们来看一个例子:
function setName(obj){ obj.name="Nicholas"; obj=new object(); obj.name="Greg"; } var person=new Object(); setName(person); alert(person.name);//"Nicholas"
*因为当函数内部重写obj时,这个变量引用的就是一个局部对象了,所以这个局部对象在函数执行完毕后立即被销毁。因此,在弹出对话框时的内容依旧是Nicholas。故而,参数是按值传递的,如果是按引用来传递的话,obj的name属性的值将会被改变。
5.检测类型
前面我们接触过的检测类型是typeof操作符,但是这只适用于基本类型的变量,对于是对象的变量,我们应当使用instanceof。语法如下:
result=variable instanceof constructor;
例如:
alert(person instanceof Object);//变量person是 Object吗? alert(colors instanceof Array);//变量colors是 Array吗? alert(pattern instanceof RegExp);//变量pattern是RegExp吗?
该操作符将返回的是布尔值
二、执行环境和作用域
执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。执行环境分为全局执行环境和局部执行环境。在web浏览器中,全局执行环境被认为是windows对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。全局执行环境直到应用程序退出后(例如关闭网页或浏览器)时才会被销毁,保存在其中的变量和函数定义也随之销毁。
作用域链是用于保证 对执行环境有权访问的所有变量和函数的有序访问,作用域链的前端,始终都是当前执行的代码所在的变量对象。如果这个环境是函数,那么这个函数就是最前的变量对象,往后就是包含在外的一个函数,或是外部变量,一次类推。
*我们的标识符解析都是沿着作用域链一级级地搜索标识符的过程,所以,全局环境中不可以调用局部环境中的变量。但是局部环境却可以使用全局变量。
(1)延长作用域链
有两个方法可以帮助我们延长作用域链:try-catch语句的catch块以及with语句,我们下面重点来了解一下with语句:
function buildUrl(){ var qs="?debug=true"; with(location){ var url=href+qs; } return url; }
with语句括号内是location对象,当在语句块中引用href的时候,实际上引用的是location.href。with语句使得location的作用域延长到了作用域链的前端。with 语句可以方便地用来引用某个特定对象中已有的属性,去除多次书写对象名的麻烦。但是不能用来给对象添加属性。要给对象创建新的属性,必须明确地引用该对象。
(2)没有块级作用域链
在作用域方面,js没有块级作用域链,这和C语言等常见的语言很不一样。js只有全局和局部两种作用域。
if(true){ var color="blue"; } alert(color);//blue
在c语言等编程语言当中,因为存在块级作用域,所以color在花括号结束时将被销毁,所以将无法输出color。但是上面例子的代码却能正常输出,这就是因为js把color变量放在了全局环境当中去。所以,color没有被销毁。
*因为js的这个特点,所以在使用 for语句的时候要格外注意:
for(var i=0;i<10;i++){ doSomething(i); } alert(i);//10
这个时候,输出的i是第十次循环时的i变量