引言

JavaScript的变量本质是松散类型的,也就是说其变量就是用于保存特定值的一个名字,变量的值和数据类型可以在脚本执行的生命周期中发生变化。这是一个很有趣很强大的特性,但是也是一个极容易出错误的地方。

两大类型

基本类型:指的是简单的数据段,有五种基本类型:Undefined,Null,Boolean,Number,String。这几种基本类型是按值访问的,操作变量时操作的是保存在变量中实际的值。

引用类型:指哪些可能由多个值构成的对象(Object)。引用类型的值是保存在内存中的对象。需要注意的是,JavaScript不允许直接访问内存中的位置,所以操作对象时,实际是操作对象的引用(这种说法是不严谨的,因为当复制保存对象的变量时,操作的是对象的引用;但是在为对象添加属性时,操作的是对象实际本身)。

变量的复制

基本类型变量的复制

当复制基本类型的变量时,实际是在变量对象上创建一个新值,然后把这个创建的新值分配给为新变量复制的位置上。所以原始变量和被赋值的变量在内存中是独立的,这两个变量可以参与任意操作而不会相互影响

var a = 'test';
var b = a;
a = 'change';
console.log(b);
//输出:test

 

引用类型变量的复制

当复制引用变量的值时,也是讲存储在变量对象中的值复制一份放在新变分配的空间中。但是这里需要理解的是,这个值的副本实际是一个指针,而这个指针指向存储在堆内存中的一个对象。上面已经说过了:对于引用类型的变量,JavaScript不允许直接操作对象,而是操作对象的引用,所以在引用变量中存储的是一个指向实际存储空间的指针。复制结束后两个变量实际引用的一个对象,所以改变其中一个变量,另一个将会受到影响。

var a = new Object();
var b = a;
a.title = 'test';
console.log(b.title);
//输出:test

保存在变量对象中的变量和保存在堆中的对象之间的关系(不要在意图片丑~)

参数传递

在ECMAScript中所有函数的参数都是按值传递的。什么意思呢?就是把函数体外面的值复制给函数内部的参数(如同把值从一个变量复制到另一个变量一样)。这样子就很好理解了。和理解了上面变量的复制,也就很好理解js中参数的传递特性了。

在向参数传递基本类型值时,被传递的值会被赋值给一个局部变量,即函数体的命名参数(在ECMAScript中就是arguments对象中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址赋值给参数。

function setName(n) {
n = 'test';
return n;
}
var a = 'haha';
var b = setName(a);
console.log(a);
console.log(b);
//输出:
//haha
//test

如果使用引用类型作为参数:

function setName(obj) {
obj.name = 'shanlei';
}
var a = new Object();
setName(a);
console.log(a.name);
//输出:shanlei

在这个函数中obj和a引用的是同一个对象,挡在setName中添加name属性后,函数外部也会有所反应。在这里有人可能会错误的认为在局部作用域中修改的对象会在全局作用域中反映出来,就是说明这个参数是按引用传递的。这个思想是错误的。可以看下面这个例子:

function setName(obj) {
obj.name = 'shanlei';
obj = new Object();
obj.name = 'Grep';
}
var a = new Object();
setName(a);
console.log(a.name);
//输出:shanlei

在这段中,我们将obj重新定义了一个对象,并且给这个新对象定义一个不同值的name属性。如果参数是按引用传递的,那么a应该会自动被修改为指向其name属性设置为'Grep'的新对象。因为如果参数是以引用传递的,那么obj和a应该是统一引用,这个引用指向堆中实际存储对象的空间。当生成一个新的对象设置新的name属性并将这个新对象给obj时,实际上就是改变了这个引用在堆中的指向,所以外部的a也就会自动改变。而输出的依旧是'shanlei',这说明在函数内部修改了参数的值,但原始的引用依旧没有改变。因为obj和a在内存中是两个独立的变量对象,这两个变量对象指向堆中同一空间,当生成新对象并传递给obj时,obj的指向发生改变,但是a的指向依旧是堆中原始的空间,所以输出的依旧是‘shanlei’。当在函数内部重写obj时,这个变量引用就是一个局部对象,且这个局部对象在函数执行完毕以后会立即销毁。

以上~~

最新文章

  1. js异步编程
  2. 初涉SQL Server性能问题(1/4):服务器概况
  3. 基于go-ceph创建CEPH块设备及快照
  4. HDU 3642 Get The Treasury 线段树+分层扫描线
  5. 【好程序员笔记分享】——iOS开发之使用TextField作为搜索框
  6. ASP.NET实现IE下禁用浏览器后退按钮办法
  7. 【转】Hibernate和IBatis对比
  8. DELPHI XE5安装
  9. 框架应用:Mybatis (三) - 关系映射
  10. 五、文件IO——dup 函数
  11. JS中的数学方法
  12. HDU 1542 矩形面积并【离散化+线段树+扫描线】
  13. g++编译多个文件
  14. web应用的乱码解决
  15. xml.sax 笔记
  16. 判断页面是app打开还是浏览器打开。cookie
  17. QT的setwindowflags的属性总结
  18. 2018.08.28 ali 梯度下降法实现最小二乘
  19. c++ sleep(windows/linux)
  20. html5中的SessionStorage 和localStorage

热门文章

  1. NoClassDefFoundError: net/sf/ezmorph/Morpher
  2. Zookeeper 源码(七)请求处理
  3. C#分布式事务解决方案-TransactionScope(转)
  4. myeclispe2014启动后报错 Subclipse talks to Subversion via a Java API that requires access to native libraries.
  5. C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数
  6. iconv用法解读
  7. AppleScript: Handler
  8. VMware安装Ubuntu
  9. 打开窗口进行选择文件(txt文件),打开所选文件,读入文件
  10. Oracle EBS R12多组织访问架构