JavaScript基本类型和引用类型

前言

ECMAScript变量可能包含两种不同数据类型的值:基本类型和引用类型。

  • 基本类型:Undefined、Null、Boolean、Number、String。这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
  • 引用类型:Object、Array、Function、Date、RegExp、Error、Map、Set…等,其他可能会很少用到。引用类型的值是保存在内存中的对象。JavaScript不允许直接访问内存中的位置,实际上是在操作对象的引用而不是实际的对象,为此,引用类型的值是按引用访问的。

动态属性

定义基本类型值和引用类型值的方法是类似的:创建一个变量并为该变量赋值。但是,当这个值保存到变量中以后,对不同类型值可以执行的操作大相径庭。对于引用类型的值,可以为其添加属性金额方法,也可以改变和删除其属性和方法。请看下面例子:

var person=new Object();
person.name="a";
alert(person.name);//a

以上代码创建了一个对象并保存在person变量中。然后,为该对象添加了一个名为name的属性,并将“a”赋值给这个属性。如果对象不被销毁或者这个属性不被删除,则这个属性将一直存在。

复制变量值

从一个变量向另一个变量复制基本类型的值时,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。例子如下:

var num1=5;
var num2=num1;

num1和num2中保存的值都是 5,但两者之间是完全独立的。之后,这两个变量参与的操作不会受到任何影响。
这里写图片描述
当一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束之后,两个变量实际上将引用同一个对象,改变其中一个变量,就会影响另一个变量。如下面例子所示:

var obj1=new Object();
var obj2=obj1;
obj1.name="a";
alert(obj2.name);//a

首先,变量obj1保存了一个对象的新实例。然后,这个值被复制到obj2中,obj1和obj2都指向的同一个对象。当为obj1添加name属性之后,由于两个变量都引用的同一个对象,所以可以通过obj2访问这个属性。
这里写图片描述

传递参数

ECMAScript中所有的函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。有些人可能会感到困惑,因为访问变量由按值和按引用两张方式,而参数智能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(arguments对象中的一个元素)。在向参数传递一个引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量会反映在函数的外部。请看下面的例子:

function addTen(num){
    num+=10;
    return num;
}
var count=20;
var result=addTen(count);
alert(count);//20,没有变化
alert(result);//30

addTen()有一个num参数(局部变量),在调用函数时,变量count作为参数被传递给函数,这个变量的值为20.在函数内部,参数num的值被加上10,但不会影响外部的count变量。如果使用对象,问题就会有点困难。再举一个例子:

function setName(obj){
    obj.name="a";
}
var person=new Object();
setName(person);
alert(person.name);//a

以上代码创建了一个对象,并将其保存在person中。然后,这个对象被传递到setName()函数中之后被复制给了obj。这这个函数内部,obj和person引用同一个对象。换句话说,即使这个对象是按值传递的,obj也会按引用来访问同一个对象。于是,当在函数内部obj添加name属性后,函数外部的person也将有所反应;因为person指向的对象在堆内存中只有一个,而且是全局对象。有很多开发人员认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们再看一看下面这个人经过修改的例子:

function setName(obj){
    obj.name="a";
    obj=new Object();
    obj.name="b";
    alert(person.name);//b
}
var person=new Object();
setName(person);
alert(person.name);//a

把person传递给setName()后,其name属性被设置为”a”。然后,又将一个新对象赋给变量obj,同时将其name属性设置为“b”。如果person是按引用传递的,那么person将会自动被修改为指向name属性值为“b”的新对象。但是,当在函数外部访问person.name时,显示的值仍然是“a”。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj时,这个变量引用的就是局部变量对象了。而这个局部对象会在函数执行完毕后立即被销毁。

检测类型

使用typeof和instanceof操作符进行类型检测。

  • 使用typeof检测基本类型,返回值如下:
var s="a";
var b=true;
var c=22;
var d;
var e=null;
var f=new Object();

alert(typeof a);//String
alert(typeof b);//boolean
alert(typeof c);//number
alert(typeof d);//undefined
alert(typeof e);//object
alert(typeof f);//object
  • 使用instanceof操作符检测引用类型
alert(person instanceof Object);//变量person是Object吗?
alert(colors instanceof Array);//变量colors是Array吗?
alert(pattern instanceof RegExp);//变量pattern是RegExp吗?

根据规定,所有引用类型的值都是Object的实例,在检测一个引用类型值和Object构造函数时,instanceof操作符始终会返回true。当然,如果使用instanceof操作符检测基本类型的值,则操作符始终会返回false,因为基本类型不是对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值