文章参考一没有留名的网友——《js的值类型和引用类型小结 文字说明与实例》。
我是带着期待验证的观点来读这篇文章的,我的观点是:引用是从编译器层面实现的一种机制,和指针不一样,指是地址指向同样的地址,而引用不是有一个指针变量,然后这个变量的值就是地址,而引用就是从编译器层面上就是把代码处理成同一个内容,但是会给引用变量加上一些功能,比如把一个变量置为NULL的时候,就会消除一个引用计数。先看网友文章:
一、拥抱JavaScript
曾经名不经传的JavaScript随着AJAX的流行而身价倍增,现在JavaScript不再仅仅是WEB开发中一个可有可无的辅助工具,甚至有了专门属于它的职位“JavaScript工程师”,那怕你仅仅是一名WEB后台开发程序员,你都必须了解JavaScript,至少在一些相关招聘职位要求上你可以看到“熟悉JavaScript优先”的字眼。甚至我还要告诉你,你将可以用JavaScript开发桌面软件,这得益于Adobe AIR的另外一种开发模式,即用HTML+CSS+JavaScript开发AIR。
二、值类型和引用类型话题
随着部分有大型面向对象语言基础朋友的介入,他们试着用JavaScript去模拟面像对象的各种特征,尽管有些模拟显得较为牵强,但也让我们见识到了JavaScript的强大与灵活性。本文暂不探讨JavaScript面向对象编程技术。就讲讲JavaScript中的两种变量类型:即值类型和引用类型,这通常又会让你联想到“堆栈”,另外还有“引用地址”或“指针”相关概念,有过Java或C#编程经验的人相信对这两种类型不陌生。下面就举例讲一下这两种类型在JavaScript中的体现、用法及注意事项。
三、JavaScript值类型和引用类型有哪些
(1)值类型:数值、布尔值、null、undefined。
(2)引用类型:对象、数组、函数。
四、如何理解值类型和引用类型及举例
我们可以用“连锁店”和“连锁店钥匙”来理解,不知道以下比喻合不合适,^-^。
(1)值类型理解:变量的交换等于在一个新的地方按照连锁店的规范标准(统一店面理解为相同的变量内容)新开一个分店,这样新开的店与其它旧店互不相关、各自运营。
【值类型例子】
1 function chainStore() 2 { 3 var store1='Nike China'; 4 var store2=store1; 5 store1='Nike U.S.A.'; 6 alert(store2); //Nike China 7 } 8 chainStore();
把一个值类型(也可以叫基本类型)store2传递给另一个变量(赋值)时,其实是分配了一块新的内存空间,因此改变store1的值对store2没有任何影响,因为它不像引用类型,变量的交换其实是交换了指像同一个内容的地址。
(2)引用类型理解:变量的交换等于把现有一间店的钥匙(变量引用地址)复制一把给了另外一个老板,此时两个老板同时管理一间店,两个老板的行为都有可能对一间店的运营造成影响。
【引用类型例子】
1 function chainStore() 2 { 3 var store1=['Nike China']; 4 var store2=store1; 5 alert(store2[0]); //Nike China 6 store1[0]='Nike U.S.A.'; 7 alert(store2[0]); //Nike U.S.A. 8 } 9 chainStore();
在上面的代码中,store2只进行了一次赋值,理论上它的值已定,但后面通过改写store1的值,发现store2的值也发生了改变,这正是引用类型的特征,也是我们要注意的地方。
andy:下边看我自己的测试。为了测试如果我new出来一个变量,当这个变量被赋值的时候是不是还是会被“牵跑”,也就是说对丢弃本身new出来的空间的引用,而去引用另一个变量的空间。
1 <!DOCTYPE html> 2 <html> 3 <script> 4 function test1() 5 { 6 var value1 = 3; 7 } 8 var test2 = new test1(); 9 test2.value1 = 4; 10 var test3 = new test1(); 11 test3 = test2; 12 test2.value1 = 5; 13 alert(test3.value1); 14 </script> 15 </html
输出结果:5
所以证明了我的猜想,虽然test3是new出来的一个新的空间,但是其实test3也不用有这个空间,只不过是引用了这个空间,test3=test2这一句,test3又引用了test2引用的空间(注意是test2引用的空间,也不是test2自己的空间哦)。也就是说var test3 = new test1();这一句话其实是做了两个动作,第一个动作是申请了一个空间,第二个动作是test3来引用这个空间。test3 = test2;这句话其实也是做了两个动作,第一个是test3弃它的引用空间而去,第二个动作是test3投奔了test2引用的空间,这时候test3最初new出来的那个空间已经没有人应用了,回收机制就可以回收了,而test2申请的那个空间现在有test3和test2两个引用。这个过程可以用一个更形象的比喻来表达:test2放飞了一个风筝,然后它手里牵着线(引用),而test3也放飞了一个风筝,也自己牵着线,test3突然放开手中的线,去牵了test2手里的线,结果放开的风筝飞了(回收程序释放了内存),test3和test2同时牵着同一个风筝,这就是引用。
再看:
1 <!DOCTYPE html> 2 <html> 3 <script> 4 function test1() 5 { 6 var value1 = 3; 7 } 8 var test2 = new test1(); 9 test2.value1 = 4; 10 var test3 = new test1(); 11 test3 = test2; 12 test2.value1 = 5; 13 test3 = 8888; 14 var test4 = 9999; 15 test3 = test4; 16 test3 = 7777; 17 alert(test4); 18 </script> 19 </html>
输出:9999
由此可见test3这时候因为赋值为了整形,所以变成了值类型,就不再是引用了。
PS:还要注意一点,编译器编译完之后就不存在变量的符号了。都是地址和指令形式了。指针变量和引用变量如果指向(引用)同一个地址的时候,编译完之后实质就是“指令和地址”中的地址部分都是指向的同一个地方,而对应的“指令和地址”中的指令不一样了,而这些指令就是编译器实现引用和指针的机制的不同。
关于内存回收的介绍:
对象废除
ECMAScript 拥有无用存储单元收集程序(garbage collection routine),意味着不必专门销毁对象来释放内存。当再没有对对象的引用时,称该对象被废除(dereference)了。运行无用存储单元收集程序时,所有废除的对象都被销毁。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的局部变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。
把对象的所有引用都设置为 null,可以强制性地废除对象。例如:
var oObject = new Object;
// do something with the object here
oObject = null;
当变量 oObject 设置为 null 后,对第一个创建的对象的引用就不存在了。这意味着下次运行无用存储单元收集程序时,该对象将被销毁。
每用完一个对象后,就将其废除,来释放内存,这是个好习惯。这样还确保不再使用已经不能访问的对象,从而防止程序设计错误的出现。此外,旧的浏览器(如 IE/MAC)没有全面的无用存储单元收集程序,所以在卸载页面时,对象可能不能被正确销毁。废除对象和它的所有特性是确保内存使用正确的最好方法。
注意:废除对象的所有引用时要当心。如果一个对象有两个或更多引用,则要正确废除该对象,必须将其所有引用都设置为 null。