变量和作用域

2018-03-01 11:00:41来源:segmentfault作者:fsrookie人点击

分享
前言

JavaScript中变量是松散类型,这样它只在特定的时间内用于保存一个特定的值。由于不存在定义某个变量必须要保存何种数据类型的规则,变量的值和其数据类型可以在脚本的生命周期内改变。


1. 基本类型和引用类型的值

JavaScript中变量可能包含两种不同类型的值: 基本类型和引用类型。 基本类型值指的是简单的数据段,而引用类型指的是那些可能多个值构成的对象。


JavaScript中有5种基本类型的值:Number, String, Null,Undefined, Boolean。这5种基本类型是按值访问的,因为可以操作保存在变量中的实际的值。


引用类型的值是保存在内存中的对象。 与其他语言不同, JavaScript不允许直接访问内存中的位置, 也就是说不能直接操作对象的内存空间。 在操作对象时, 实际上是操作对象的引用而不是实际的对象。因此,引用类型的值是按引用访问的。


注意: 当复制保存着对象的某个变量时, 操作的是对象的引用。 但在为对象添加属性的时候,操作的实际的对象


1.1 动态的属性

定义基本类型值和引用类型值得方式是类似的:创建一个变量并为该变量赋值。对于引用类型的值来说,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。但是基本类型的值却不行。


var person = new Object()
person.name = 'Nicholas'
alert(person.name) //'Nicholas'

如上,我们创建一个对象,并保存在person。然后为该对象添加一个name属性,最后通过person.name可以访问这个新属性,如果对象不被销毁或者这个属性不被删除,则这个属性将一直存在。


如果我们给基本类型的值添加属性,虽然不会导致错误,但是这样是不合理的:


var name = 'Nicholas'
name.age = 27
alert(name.age) //undefined
1.2 复制变量值

除了保存的方式不同,从一个变量向另一个变量复制基本类型值和引用类型值时也存在不同。如果一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。


var num1 = 5
var num2 = num1


当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值得副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量:


var o1 = new Object()
var o2 = o2
o1.name = 'Nicholas'
alert(o2.name) //Nicholas


1.3 传递参数

ECMAscript中所有的函数参数都是按值传递的,也就是说,把函数外部的值复制给函数内部参数,就和把值从一个变量复制到另一个变量一样。基本类型的传递如同基本类型变量的复制一样,而引用类型值得传递如何引用类型变量的复制一样。


在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,就是arguments对象中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址复制一个给局部变量,因此这个局部变量的变化会发生在外部。但是这样总会感觉参数还是通过按引用传递的。下面这个例子可以证明是按值传递:


var person = new Object()
function setName(obj) {
obj.name = 'Nicholas'
obj = new Object()
obj.name = 'Greg'
}
setName(person)

如果person是按引用传递的,那么person会被改成一个新对象,但是当你继续访问person.name的时候返回的是'Nicholas'。这说明即使在函数内部修改了参数的值,但是原始的引用仍然保持未变。实际上当函数内部重写obj时,这个变量引用的就是一个局部对象了,而这个局部对象会在函数执行完毕后销毁。


2. 执行环境和作用域

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有与之关联的变量对象。执行环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。


每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中,而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。


当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行代码所在的环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即argument对象。作用域链的下一个变量对象则来自下一个包含环境。这样一直延续到全局环境。


标识符解析是沿着作用域链一级一级的搜索标识符的过程。


2.1 延长作用域链

有些语句可以在作用域链的前端临时增加一个变量对象, 该变量对象会在代码执行后被移除。在两种情况下会发生这种现象,具体来说就是当执行流进入下列任何一个语句时,作用域链会得到加长。

try-catch的catch块
with语句

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台