① 这种说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象。
var person = new Object(); person.name = "Nicholas"; alert(person.name); //"Nicholas"运行一下
var name = "Nicholas"; name.age = 27; alert(name.age); //undefined运行一下
var num1 = 5; var num2 = num1;在此,num1 中保存的值是5。当使用num1 的值来初始化num2 时,num2 中也保存了值5。但num2中的5 与num1 中的5 是完全独立的,该值只是num1 中5 的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。图4-1 形象地展示了复制基本类型值的过程。
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"首先,变量obj1 保存了一个对象的新实例。然后,这个值被复制到了obj2 中;换句话说,obj1和obj2 都指向同一个对象。这样,当为obj1 添加name 属性后,可以通过obj2 来访问这个属性,因为这两个变量引用的都是同一个对象。图4-2 展示了保存在变量对象中的变量和保存在堆中的对象之间的这种关系。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScript 的概念来说,就是arguments 对象中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。请看下面这个例子:
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"这个例子与前一个例子的唯一区别,就是在setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name 属性。在把person 传递给setName()后,其name 属性被设置为"Nicholas"。然后,又将一个新对象赋给变量obj,同时将其name属性设置为"Greg"。如果person 是按引用传递的,那么person 就会自动被修改为指向其name 属性值为"Greg"的新对象。但是,当接下来再访问person.name 时,显示的值仍然是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
可以把ECMAScript 函数的参数想象成局部变量。
var s = "Nicholas"; var b = true; var i = 22; var u; var n = null; var o = new Object(); alert(typeof s); //string alert(typeof i); //number alert(typeof b); //boolean alert(typeof u); //undefined alert(typeof n); //object alert(typeof o); //object运行一下
result = variable instanceof constructor如果变量是给定引用类型(根据它的原型链来识别;第6 章将介绍原型链)的实例,那么instanceof 操作符就会返回true。请看下面的例子:
alert(person instanceof Object); // 变量person 是Object 吗? alert(colors instanceof Array); // 变量colors 是Array 吗? alert(pattern instanceof RegExp); // 变量pattern 是RegExp 吗?根据规定,所有引用类型的值都是Object 的实例。因此,在检测一个引用类型值和Object 构造函数时,instanceof 操作符始终会返回true。当然,如果使用instanceof 操作符检测基本类型的值,则该操作符始终会返回false,因为基本类型不是对象。
使用typeof 操作符检测函数时,该操作符会返回"function"。在Safari 5 及之前版本和Chrome 7 及之前版本中使用typeof 检测正则表达式时,由于规范的原因,这个操作符也返回"function"。ECMA-262 规定任何在内部实现[[Call]]方法的对象都应该在应用typeof 操作符时返回"function"。由于上述浏览器中的正则表达式也实现了这个方法,因此对正则表达式应用typeof 会返回"function"。在IE 和Firefox 中,对正则表达式应用typeof 会返回"object"。