由于最近项目当中使用EXTJS这样一种前端框架,所以就离不开JavaScript得使用,所以最近我就在自己研究学习JavaScript,目的是为了方便开发的顺利进行。但是在实际开发当中,我发现JavaScript是一门很简单就能上手的语言,但是真正使用起来我们就会发现他作为一门弱类型的语言,从某种角度来看可能是一个既有趣有强大的功能,但是也是容易出问题的特性。
楼主本人也是处于学习这门语言当中,所以写一篇博客也是为了巩固自己的知识,同时也是为了和大家一起分享我对于学习当中一些大家都用过但是不一定在意过的小细节进行一个整理分析,如果有不对的地方,还是希望大家提出给予修正,共同成长。
好了闲话不多说,我们赶紧进入基本类型和引用类型的讲解。
首先我想给大家打一个比方,每个人都有一个身份证,就像下图的这个白富美的身份证。
如果白富美要去办事情,有的可能会问白富美要身份证的复印件,有的可能会问白富美要身份证的原件,这里的复印件白富美可以做到给每个人都印一份,谁问白富美要白富美就可以复印一份给谁,这个就不会互相影响大家对于这个复印件的使用,但是身份证的原件不同,他永远都在白富美的手里,不会再你我的手里,谁要用只能和白富美进行联系,并且由白富美拿给他,如果同时有另一个人要用也要通过白富美。而联系白富美就需要打电话,所以这个电话就相当于一个指针,他永远指向白富美(除非白富美受够了换号了),而身份证就是白富美这个对象的一个属性值,而你我给他拨打电话的人就是引用类型,我们的电话薄中永远存储着一个指向白富美的电话,用来获取他这个对象,但是我们作为引用类型却不能和白富美一样持有他的身份证,只能持有他的电话号码,也就是持有一个对象的引用。
看起来是不是有一种MDZZ的感觉,要和楼主说太长不看?
下面我们一起看一个例子,当然如果你能看懂,可以直接说一句楼主MDZZ。
var a=1; var b=a; alert(b);//你猜这时候b等于多少? a = 3; //你猜我要干嘛? alert(b);//知道这时候b是多少么?</span>
看懂了么!!!!不管你看没看懂我们继续接着看另一个问题
var Mother = new Object(); Mother.name = 'itnao'; var son1 = Mother; var son2 = Mother; alert(son1.name); alert(son2.name); Mother.name = 'tomommi'; alert(son1.name);//猜猜这里能显示什么结果 alert(son2.name);//同上 delete Mother.name;//猜猜什么类型能用这个方法 alert(son1.name);//猜猜能输出什么结果
好了我的问题提出了,接下来我们就要一一解答上面的问题。
一. 基本类型
概念:基本类型包含(undefined,null,num,string,boolean)基本类型的访问是按值进行访问的,也就是说我么可以操作保存到变量里面的实际值。
1.基本类型是存在栈内存当中的
我们先来建立几个变量
var a=1; var b=2;
我们来看一下在栈内存中他们是如何存储的
这里可以看到,在栈内存中,存储了变量的标识符和变量的实际值。
2.基本类型的值是无法改变的
还是先看代码
var name = "ITNAO"; name.toLowerCase();//进行小写操作 alert(name);//ITNAO
实际上最后name还是等于"ITNAO",而调用的.toLowerCase()方法返回的是一个新的字符串,并不会改变name中的值。
我们还无法给基本类型的值添加属性和方法
var name="itnao"; name.age=21; alert(name.age);//undefined
3.基本类型的对比是值得对比,这个是区别于引用类型的
var a = "a"; var b = "a"; alert(a == b);//true
因为基本类型的值是真实的值,所以当对比的时候,其实就是对比栈内存中的值,但是引用类型就大不相同,具体如何我们接着往下看。
二.引用类型
概念:JavaScript除了基本类型(undefined,null,num,string,boolean)外,都可以叫做引用类型,引用类型的值是保存在内存个中的对象。与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象的时候,实际上是操作对象的引用而不是实际的对象,为此,引用类型的值是按引用传递的。当然这个说法也不是很严谨,因为当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性的时候,操作的实际上是对象。
1.引用类型是分别存放在栈内存和堆内存中的
我们定义两个引用类型
var Mother = {age:21}; var person = {age:22};
他们在栈内存中存储了变量的标识符和指向堆内存值中该对象的指针(也就是引用,JavaScript访问引用类是的实质是访问引用类型的引用),而在堆内存当中存储的是对象,我们知道JavaScript是不允许我们直接操作内存中的位置也就是说不能直接操作对象的内存空间,那么我们如何操作呢,我们就需要操作对象的引用而不是对象。
这个指向堆内存的指针也可以理解为,对象的引用,我们通过这个对象的引用来访问存储于堆内存中的对象。
2.引用类型的值是可以动态改变的
我们来看一下代码
var Mother = new Object();//创建了一个Object的实例 Mother.name = 'itnao';//可以给他赋值 Mother.sayName = function(){alert(Mother.name);}//也可以给他写入一个方法 Mother.sayName();//itnao delete Mother.name; Mother.sayName();//undefined
通过上述代码我们就可以看到,我们可以动态的给引用类型添加属性和方法,并可以随时的删除属性。
3.引用类型的大小比较
引用类型的大小比较不同于基本类型,我们先来回顾一下基本类型的比较
var Mother = "{}"; var father = "{}"; alert(Mother == father);//true
我们可以看到结果是true,因为Mother和father是字符串类型,这两个比较可以看出是比较的是变量中存储的实际值。
var Mother = {}; var father = {}; alert(Mother == father);//false
这里我们对Mother和father都使用了对象字面量的方式,简写的建立了两个对象,但是我们可以看出他俩却不相等,这是因为引用类型是按引用访问的,而引用类型存储在栈内存中的值是堆内存的地址,所以两个对象的引用的地址是不可能相同,所以结果也就不可能相同。
是不是感觉楼主很墨迹,还不赶紧进入主题讲讲开头问的问题的答案,其实楼主就是这样的人,不服气来打我啊!!!!!
三.解决问题
我们开始解释第一个问题
var a=1; var b=a; alert(b);//你猜这时候b等于多少? a = 3; //你猜我要干嘛? alert(b);//知道这时候b是多少么?</span>
再解释之前我先把这个问题的流程图给大家看一下
我们可以看到,我们在声明基本类型a = 1的时候,栈内存当中会创建一个变量标识符和变量的值,当我们让b=a的时候,我们会在栈内存当中创建一个标识符b并将1复制给b,这个也可以说b中的1是a当中1的一个副本,这就相当于白富美的复印件,所以复印件之间是不会发生影响的,你可以撕了你手中的复印件,可是我手中的还没发生变化,所以你改变a的值不会影响到b当中的值。所以结果大家应该很清楚了。
实际解答:如果从一个变量向另一个变量复制基本类型值得时候,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
我们开始解答第二个例子的问题
var Mother = new Object(); Mother.name = 'itnao'; var son1 = Mother; var son2 = Mother; alert(son1.name); alert(son2.name); Mother.name = 'tomommi'; alert(son1.name);//猜猜这里能显示什么结果 alert(son2.name);//同上 delete Mother.name;//猜猜什么类型能用这个方法 alert(son1.name);//猜猜能输出什么结果
还是先给大家看看图吧
如果我们想问白富美要他的身份证,我们需要给白富美打电话,我们的通讯录里面就会存储白富美的电话,这个电话就相当于指向白富美的指针,100个找白富美的人,就会有一百个相同的白富美的电话,这个电话号码就相当于是引用,你只能通过给白富美打电话来找到他,而不能直接就找到,因为很可能你压根不认识他,也不知道他现在在哪里。所以上图的关系图我们可以看出,变量Mother保存了一个对象的新实例。然后这个值被复制到了son1和son2中,换句话说Mother,son1和son2都指向同一个对象,所以牵一发而动全身,我们修改了Mother的name,其他两个也会跟着改变。
实际解答:当一个变量向另一个变量复制引用类型的值时同样也会将存储在变量中的值复制一份放到为新变量分配的空间中。不同的是,这个值得副本实际上是一个指针,而这个指针指向内存在堆中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象。因此,在改变其中一个变量,就会影响另一个变量。