第5章 变量与对象
5.1 变量的声明
变量的功能为持有某个值,或者用来表示某个对象。
// 如果一个变量在声明之后没有进行赋值,它的值就会是undefined。
// 对同一个变量重复进行声明是不会引起什么问题的,原有的值也不会被清空。
var hzh1 = 7;
console.log("输出hzh1的值:");
console.log(hzh1);
// 即使对同一个变量重复进行声明
var hzh1;
// 它的值也不会发生改变
console.log("输出变量hzh1的值:");
console.log(hzh1);
// 如果变量 a 具有某个可以被转换为 true 的值就直接使用,
// 否则就把 7 赋值给a
console.log("输出变量hzh2的值:");
var hzh2 = hzh2 || 7;
// 如果 a 是一个已经被声明且赋值的变量,则不会有任何效果;
// 而如果没有被声明过,则会在声明的同时对其进行赋值操作。
/*************************************************************/
// 如果变量 b 没有被声明过,将会引起 ReferenceError 异常。
// 不过,也不能说它绝对就是错的。
// 这是因为,如果能确保在这条代码之前就已经对变量 b 进行了声明,
// 这段代码的作用就变为了判定变量 b 的值的真假,这样就没有问题了。
var hzh3 = hzh4 || 7;
console.log("输出变量hzh3的值:");
console.log(hzh3);
5.2 变量与引用
对象的概念很好地说明了变量是一种拥有名称的客体。对象本身是没有名称的,之所以使用变量,是为了通过某个名称来称呼这样一种不具有名称的对象。
变量又分为基本类型的变量(值型变量)与引用类型的变量。由于在 JavaScript 中,变量是不具有类型的,因此从语法标准上来看,两者并没有什么不同。不过,在 JavaScript 中仍然有对象的引用这一概念。
所谓“引用”,可以认为是一种用于指示出对象的位置的标记。如果你熟悉 C 语言,把它理解为是和指针等价的东西也没有问题。不过,引用不支持那些可以对指针进行的运算。引用这一语言功能只有指示位置信息的作用。准确地说,对象的赋值其实是将对象的引用进行赋值。
为了更好地解释引用这一概念,这里对引用类型的变量和值型变量进行比较。将基本类型的值赋值给变量的话,变量将把这个值本身保存起来。这时,可以将变量简单地理解为一个装了该值的箱子。变量本身装有所赋的这个值,所以能够将该值从变量中取出。如果在右侧写上一个变量,这一变量的值将被复制给赋值目标处(左侧)的变量。
var a = 123; // 将数值123赋值给变量a
var b = a; // 将变量a的值(数值123)赋值给变量b
console.log("第一次输出变量a的值:");
console.log(a);
console.log("第一次输出变量b的值:");
console.log(b);
b++;
console.log("");
console.log("第二次输出变量a的值:");
console.log(a);
console.log("第二次输出变量b的值:");
console.log(b);
var a = { x:1, y:2 }; // 将对象的引用赋值给变量a
var b = a; // 将变量a的值(对象的引用)赋值给变量b
console.log("输出变量a的值:");
console.log(a);
console.log("输出变量b的值:");
console.log(b);
console.log("*******************************************");
// 如果像下面这样,改变了变量 b 所引用的对象,
// 那么这一改变也会体现在变量 a 之中,
// 这是因为这两个变量通过引用而指向了同一个对象
b.x++; // 改变变量b所引用的对象
console.log("输出变量b的x属性:");
console.log(b.x); // 变量b所引用的对象
console.log("输出变量a的x属性:");
console.log(a.x); // 可以发现变量a所引用的对象也被改变
// 对于引用类型的变量,
// 整个过程中发生改变的其实是其引用的对象,而不是该变量的值。
// 引用类型的变量具有的值就是引用(值),
// 这个值将在赋值的时候被复制
var a = { x:1, y:2 };
var b = a; // 变量a与变量b引用的是同一个对象
a = { x:2, y:2 }; // 改变了变量a的值(使其引用了另一个对象)
console.log("输出变量b的x属性:");
console.log(b.x); // 变量b所引用的对象没有发生改变
// 在 JavaScript 中,赋值运算总是会把右侧的值复制给左侧。
// 对于引用类型的变量来说也是一样,会将引用(用于指示对象的一种值)赋值给左侧。
// 函数调用过程中的参数也是这样的执行方式。
5.2.1 函数的参数(值的传递)
5.2.2 字符串与引用
5.2.3 对象与引用相关的术语总结
5.3 变量与属性
5.4 变量的查找
5.5 对变量是否存在的校验
5.6 对象的定义
5.6.1 抽象数据类型与面向对象
如果从形式上来定义 JavaScript 的对象,它就是一种属性的集合。所谓属性,即名称与值的配对。属性值可以被指定为任意类型的值,包括数组或其他的对象,都没有问题。
对于对象有一种很常见的定义,即它是一种数据和操作(子程序)的结合。这一定义可以理解为,将面向对象看作一种抽象数据类型的表现形式。
面向对象的 3 要素,即封装、继承与多态吧。如果这样理解的话,面向对象程序设计的焦点就在于对象的执行方式,并将执行方式的共性定义为一种类型。
在这一语境中,常常使用类这一术语来表达类型的含义。也有些语言会把执行方式与其实现分开,将执行方式定义为接口。接口的实例(实体)被称为对象,可以对其进行指定的操作。
5.6.2 实例间的协作关系与面向对象
另一种面向对象程序设计的观点认为,与其考虑执行方式之间的共性,更应该关注实例之间的协作关系,即所谓的对象是进行消息收发的实体。对象收到消息之后将会对其作出响应。从实现的角度来看,消息的实质就是通过对方法(函数)进行调用,将对消息的响应分派给方法来处理。从本质上来说,面向对象这一术语只不过是一种在高于内部实现的语境中所使用的、较为抽象的概念而已。打个比方,可以把消息当作一种通信协议,把对象当作一个 Web 应用。
5.6.3 JavaScript 的对象
JavaScript 语言所支持的面向对象与后者的理解更为相近。在JavaScript 中,一切都是对象。对象之间的协作(消息收发)通过属性访问(以及方法的调用)来实现。而对象之间的共性,则是通过继承同一个对象的性质的方式来实现。JavaScript通过基于原型的形式来实现继承。
一旦要对面向对象的概念进行说明,事情就会变得很抽象。如果只考虑具体该如何使用 JavaScript 的对象,就不必考虑那么多复杂的问题。只需要考虑最核心的内容,将其理解为在程序中可以进行操作的数据的一种扩充即可。此外,还可以通过函数方法的形式来表示对数据进行操作的子程序。这种想法的核心就是将对象的功能进行拆分并分别进行处理。分割本身也只不过是一种手段。毕竟,面向对象方法的最终目的是降低程序的复杂程度。