直接开门见山,上代码!
var a = 1;
function func(args) {
args = 10;
console.log("args:"+args);
}
func(a);
console.log("a:"+a);
/**
args:10
a:1
**/
var a = {
'x':1
};
function func(args) {
args.x = 10;
console.log("args.x:"+args.x);
}
func(a);
console.log("a.x:"+a.x);
/**
args.x:10
a.x:10
**/
PS:就这?有点基础的程序员都会!!
这两道题确实没人会回答错。。。让我想写这篇问题的是下面这道题。
var a = {
'x':1
};
function func(args) {
args = 10;
console.log("args:"+args);
console.log("args.x:"+args.x);
}
func(a);
console.log("a:"+a);
console.log("a.x:"+a.x);
这里可能就有人会错了。有人觉得变量a的值是10,a.x的值是undefined,当然也有人觉得a的值还是之前声明的值,其属性也没变化。为了解决这个疑惑,所以有了这篇文章。
先说说上面例子的运行结果:
args:10
args.x:undefined
a:[object Object]
a.x:1
在解释答案之前,我们需要重新回头看一下一些基础知识。
js的数据类型
js的数据类型分两种:
基本类型:undefined,null,number,boolean,string,symbol
引用类型:object(包含 function、Array、Date等)
基本类型
存储
基本数据类型是存放在栈区的。
var name = "jozo";
var city = "guangzhou";
var age = 22;
那么它的存储结构如下图:
栈区包括了变量的标识符和变量的值。
赋值
基本数据类型的赋值是简单赋值。如果一个变量向另一个变量赋值基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
var a = 10;
var b = a;
a++;
console.log(a)//11
console.log(b)//10
上面的代码中,a中保存的值是10.当使用a的值来初始化b时,b中也保存了值10.但b中的10和a中的10是完全独立的.b中的值知识a中值的一个副本.所以这两个变量可以参与任何操作而不会相互影响.如下图:
比较
基本数据类型的比较是值的比较。
var person1 = '{}';
var person2 = '{}';
console.log(person1 == person2); // true
var a = 10;
var b = a;
a++;
console.log(a == b); // false
引用类型
储存
引用类型是同时保存在栈区和堆区中的。引用类型的存储需要在内存的栈区和堆区共同完成,栈区保存变量标识符和指向堆内存的地址,堆区存放具体的值。
赋值
引用类型的赋值是对对象引用。
var a = {};
var b= a;
a.name = "change";
console.log(a.name)//change;
console.log(b.name)//change
b.age = 29;
console.log(a.age)//29
console.log(b.age)//29
当从一个变量向另一个变量赋值引用类型的值时,同样也会将储存在变量中的对象的值复制一份放到为新变量分配的空间中.引用类型保存在变量中的是对象在堆内存中的地址,所以,与基本数据类型的简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存的一个对象.那么赋值操作后,两个变量都保存了同一个对象地址,而这两个地址指向了同一个对象.因此,改变其中任何一个变量,都会互相影响。
因此,引用类型的赋值其实是对象保存在栈区地址指针的赋值,所以两个变量指向同一个对象,任何变量对该对象的操作都会互相影响。
比较
引用类型的比较是引用的比较,也就是地址指针的比较。
var person1 = {};
var person2 = {};
console.log(person1 == person2)//false
上面例子中,两个对象看起来一摸一样,但是却不相等!因为引用类型的比较是引用的比较,换句话说,就是比较两个对象保存在栈区的指向堆内存的地址是否相同,此时,虽然person1和person2看起来都是一个空对象{},但是他们保存在栈区中的指向堆内存的地址却是不同的,所以两个对象不相等。
js函数传参
js函数传参分两种情况:按值传递和按引用传递。
这里需要注意的是,在红宝书的中,有这么一句话“ECMAScript中所有函数的参数都是按值传递的”。那为什么我说的是两种?之所以这里说俩种,是因为结合引用传参更容易理解。不管是按值传递和按引用传递,归根结底到底都是按值传递,因为按引用传递中传递的是一个地址指针,这其实也是一个值。
值传参针对基本类型,引用传参针对引用类型,传参可以理解为复制变量值。基本类型复制后俩个变量完全独立,之后任何一方改变都不会影响另一方;引用类型复制的是引用(即指针),之后的任何一方改变都会映射到另一方。简单来说,传参就是把函数外部变量的值复制给函数内部的变量(函数参数),就和把值从一个变量复制到另一个变量一样。
按值传递
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count); //按值传递 num = count
alert(count); // 20, 没变化
alert(result); // 30
很好理解,因为是按值传递的,传递完后俩个变量各不相干!
按引用传递
var a = [1];
function b(args){
var c = args
c.push(2);
};
console.log(a); // [1,2]
如果理解了文章之前引用类型赋值那一块的知识,那这里其实也很好理解。
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person); // obj = person
alert(person.name); // "Nicholas"
当 var person = new Object(); 时,可以用下图表示变量和对象的关系:
当调用函数 setName(person); 时,下图可以表示全局变量person和局部变量obj的关系:
以上代码中创建一个对象,并将其保存在变量person中。然后,这个变量被传递到setName(obj)函数中之后就被复制给了obj。在这个函数内部,obj和person引用的是同一个对象。换句话说,即使ECMAScript说这个变量是按值传递的(此时的值是地址指针),但obj也会按引用来访问同一个对象。于是,在函数内部为obj添加name属性后,函数外部的person也将有所反应;因为这时的person和obj指向同一个堆内存地址。
看完这两个传参类型,我们来试一下下面的例子,看看你能不能答对?
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name);
能算出person.name的值吗?我来一步一步分析吧。
第一步,person = new Object();的时候,栈内存有个变量叫person,其值只想堆内存里面的Object对象(即{})。
第二步,setName(person);的时候,变量person拷贝的它的值,赋予了参数obj,此时person ==obj,都是指向同一个对象。
第三部,obj.name = "Nicholas";的时候,修改了对象的属性,由于此时person也指向这个对象,所以person.name的值变成了"Nicholas"。
第四步,obj = new Object();的时候,这是关键的一句代码,new Object()生成了一个新对象,存放在堆内存中,其指针地址赋值给了obj。也就是说,这时候的obj的值变化了,不再跟person的值相同。所以这时候person和obj分别指向两个不同的对象。
第五步,obj.name = "Greg";的时候,不管对obj的属性做什么样的修改,都不会影响到person所指向的对象,因为它们现在毫不相干。
第六步,alert(person.name);的时候,person的属性name的值由于在第三步的时候被修改成了"Nicholas",所以输出的值就是"Nicholas"。
现在再回头看看这个例子,你就懂了。
var a = {
'x':1
};
function func(args) {
args = 10;
console.log("args:"+args);
console.log("args.x:"+args.x);
}
func(a);
console.log("a:"+a);
console.log("a.x:"+a.x);
总结
授人以鱼不如授人以渔,本文没总结。
靠自己总结出来的东西才能更加深理解,不懂就把文章多看几遍。
【ps:明明是作者自己偷懒不写。】
好了,本文到此就写完了。