今天从新看了一下《javascript高级程序设计》,其中讲到了javascript中的值传递和值引用,因此就本身研读了一下,可是刚开始没有明白函数中的参数只有值传递,有的场景好像参数是以引用的方式传递的,可是实际上却不是,那究竟是怎么回事,或者是函数中的传值是值传递仍是值引用呢,下面来对书上给出的例子作一个图解,这样可以更好的解释这个问题。有顿悟的感受。javascript中貌似共有8种数据类型,包括了字符串类型,数值类型,布尔类型,undefined类型,null类型,对象,数组,函数;
1. 值传递,书上呀,各类的都说基本类型的都是值传递;javascript
基本类型:Number Boolean String Undefined Null 这5种就是javascript种的基本数据类型了;java
//基本类型演示: var a = 1; //数值型 var b = true; //布尔型 var c = undefined; //undefined类型 var d = null; //null类型 var e = 'helloworld'; //字符串类型 var a1 = a; a1 = 2; console.log(a1); // 2 console.log(a); // 1 var b1 = b;b1 = false; console.log(b1); //false console.log(b); //true var c1 = c; c1 = ' '; console.log(c1); //'' console.log(c); //undefined var d1 = d; d1 = ''; console.log(d1); //'' console.log(d); //null var e1 = e; e1 = 'i love you!'; console.log(e1); //'i love you' console.log(e); //'helloworld'
上面代码的结果说明了,以上所列几种都是值传递方式的,即原始值不会被改变,原变量与新变量之间在完成赋值,建立原始值的副本以后就没有任何关联了;数组
2. 值引用,书上呀,各类的都说引用类型的都是值引用;函数
引用类型:Object Array Fucntion spa
//1. 对象 var a = {}; var b = a; b.name = '我是b'; console.log(a.name); //'我是b' //2. 数组 var c = []; var d = c; d[0] = '2'; console.log(c); //['2'] //函数 var e = function(){ console.log('我是e函数'); } var f = e; f = function(){ console.log('我是f函数');} console.log(e); functioon(){ console.log('我是f函数');}
新变量中的行为所产生的变化,会同时在原始变量上表现出来,说明原变量在赋值给新变量的时候是将其引用赋值给了新变量,这样他们同时指向同一个引用,这样两个变量中的任何一个的动做所引发的变化都会同时在另外一个变量中反应出来。设计
3. 函数传递参数,书上呀,各类的都说是值传递;对象
先来简单的看看这个例子:blog
function setName(obj){ obj.name = 'Nicholas'; } var person = new Object(); setName(person); console.log(person.name); //‘Nicholas'
行为:ip
1. 在内存中建立一个对象,并把这个对象的引用赋值给person变量
2. 将变量person传入函数setName()内存
3. 函数setName()为obj增长一个name属性,并赋值为’Nicholas‘
4. 访问person的name属性,并将其打印在控制台
在访问person的name属性时,获得的结果是'Nicholas'。可是,这个场景让人感受好像是值引用;看看下图是程序运行时在内存中的一些变化:
解析:由于函数中的参数是以值传递方式传递的,obj是函数setName()里面的一个局部变量,是在调用函数的时候才发生参数的传递的,调用函数时,person将其拷贝了一份并赋值给了obj,二者是独立的,(由于 person是对象类型的数据,其本质是包含了建立它的对象的引用,在赋值给obj时,赋给obj的也是其原始对象的引用),可是因为二者指向的是同一个引用,因此在为obj添加name属性,并赋值 为“Nicholas”时,操做的不是obj自己的属性,而是操做的其引用Object,而person引用的也是同一个Object,因此在访问person的name属性时,根据javascript 面向对象的原型链规则,首先会检查person 自己是否有name属性,由于person没有name属性,因此继续往上搜索,在其引用的Object上面找到了name属性,因此person.name返回的是其引用的name属性,因此person.name的值是“Nicholas”。
再来看看下面的这个例子
function setName(obj){ obj.name = 'Nicholas'; obj = new Object(); obj.name = 'Grey'; } var person = new Object(); setName(person); console.log(person.name); //‘Nicholas'
行为:
1. 在内存中建立一个对象,并把这个对象的引用赋值给person变量
2. 将变量person传入函数setName()
3. 函数setName()为obj增长一个name属性,并赋值为’Nicholas‘
4. 在内存中再新建一个对象,并把这个对象的引用赋值给person变量
5. 为obj增长了一个name属性,并赋值为’Grey‘
4. 访问person的name属性,并将其打印在控制台
解析:上个例子中解释了为何person.name为’Nicholas‘,这个例子在其基础上添加了两行代码,来看看这两行代码作了什么:
在obj增长了name属性后,又在内存中建立了一个新的Object,并且把它赋值给了obj,根据引用类型的赋值,是把这个新的Object的引用传递给了obj,这点是毫无疑问的。此时obj与原来的对象的引用被新的对象的引用所取代,因此obj实际上与person已经没有任何关系了,此时将obj的name属性值改成“Grey”,其实name属性是不存在的,因此作的不是简单的改变属性的值,而是添加属性,并添加属性值。这个行为与person的引用Object是没有任何关系的,因此person的引用Object中的name也没有发生变化,仍是‘Nicholas’。当setName()函数执行完,obj这个局部变量也就不复存在了,可是这对于person来讲也是无所谓的。因此当setName()函数执行结束后,访问person的name属性时,如上仍是其引用Object的name属性,因此仍是‘Nicholas’。
若是按照上例的猜测,函数中的参数是值引用方式,那么咱们能够先在脑海里运行一下,会获得什么结果呢?
console.log(person.name) 会在控制台上打印出 ’Grey‘,可是显然结果不是这样的,最终输出的是’Nicholas‘,那是否是咱们的猜测错了,函数的参数是值传递方式呢?看看下面的图
因此:基本类型是值传递,引用类型是值引用,函数参数是值传递的说法是彻底正确的。
感受还有点儿细节须要在扣一下,后面在改改。