JavaScript 第一节 变量

来源:互联网 发布:国外p图软件 编辑:程序博客网 时间:2024/05/21 10:30

第一节 变量

  1. 变量声明
    在JavaScript程序中,使用一个变量之前应当声明一个变量。变量是使用关键字var来声明的。如果未在var声明语句中给变量指定初始值,那么虽然声明了这个变量,但在给它存入一个值之前,它的初始值就是undefined。注意:如果你之前编写过诸如C或Java的静态语言,你会注意带在JavaScript的变量声明中并没有指定变量的数据类型,JavaScript变量可以是任意数据类型。
    重复的声明和遗漏的声明
    使用var语句重复声明变量是合法且无害的。如果重复声明变量带有初始化器,那么这就和一条简单的赋值语句没什么两样。如果你试图读取一个没有声明的变量的值,JavaScript会报错。在ECMAScript 5严格模式中,给一个没有声明的变量赋值也会报错。然而从历史上讲,在非严格模式下,如果给一个未声明的变量赋值,JavaScript实际上会给全局对象创建一个同名属性,并且它工作起来像一个正确声明的全局变量。
  2. 变量作用域
    一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是有定义的。然而在函数内声明的变量只在函数体内有定义。它们是局部变量,作用域是局部性的。函数参数也是局部变量,他们只在函数体内有定义。在函数体内,局部变量的优先级高于同名的全局变量。声明局部变量时必须使用var语句。函数定义是可以嵌套的,由于每个函数都有它自己的作用域,因此会出现几个局部作用域嵌套的情况。
    (1)函数作用域和声明提前
    在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明他们的代码段之外是不可见的,我们称之为块级作用域(block scope),而JavaScript·中没有块级作用域。JavaScript取而代之地使用了函数作用域(function scope):变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
    JavaScript的函数作用域是指在函数内声明的所有变量的在函数体内始终都是可见的。有意思的是,这意味着变量在声明之前甚至已经可用。JavaScript·的这个特性被非正式地称为声明提前(hoisting),即JavaScript函数里声明的所有变量(但不涉及赋值)都被“提前”值函数体的顶部(这步操作是在JavaScript引擎的“预编译”时进行的),看一下如下代码:
var scope = "global";function f(){    console.log(scope);//输出"undefined"    var scope = "local";//变量在这里赋值,但变量本身在函数体内任何地方均是有定义的    console.log(scope);//输出"local"}
上述过程等价于:将函数内的变量声明“提前”至函数体顶部,同时变量初始化留在原来的位置。      

在具有块级作用域的编程语言中,在狭小的作用域里让变量声明和使用变量的代码尽可能靠近彼此,通常来讲,这是一个非常不错的编程习惯。由于JavaScript没有块级作用域,因此一些程序员特意将变量声明放在函数体顶部,而不是将声明靠近放在使用变量之处。这种做法使得他们的源代码非常的清晰地反映了真实的变量作用域。
(2)作为属性的变量
当声明一个JavaScript全局变量时,实际上是定义全局对象的一个属性。当使用var声明一个变量时,创建的这个对象是不可配置的,也就是说这个变量无法通过delete运算符删除。如果没有使用严格模式给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。以这种方法创建的变量是全局对象的正常的可配置属性,并可以删除他们。JavaScript全局变量是全局对象的属性,这是在ECMAScript规范中强制规定的。对于局部变量则没有如此规定,但我们可以想象得到,局部变量当做跟函数调用相关的某个对象的属性。ES3规范称该对象为“调用对象”,ES5规范称为“声明上下文对象”。
(3)作用域链
JavaScript是基于词法作用域的语言:通过阅读包含变量定义在内的数行源码就能知道变量的作用域。全局变量在程序中始终都是有定义的。局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的。
如果将一个局部变量看做是自定义实现的对象的属性的话,那么可以换个角度来解读变量作用域。每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程被称作“变量解析“),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个属性。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。
在JavaScript的最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链由一个全局对象组成。在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,作用域链上至少有三个对象。理解对象链的创建规则是非常重要的。当定义一个函数时,它实际上保存一个作用域链。当调用这个函数,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的”链“。对于嵌套函数来讲,事情变得更加有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。

这是我第一次写博客,参照了许多文献。看起来很稚嫩,很不成熟,但是我相信我会写出好文章的。嘿嘿

原创粉丝点击