javascript中的【值类型】和【引用类型】

时间:2023-02-17 22:49:12

  文章参考一没有留名的网友——《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。