好家伙,JS基础接着学,
本篇内容为《JS高级程序设计》第四章学习笔记
ECMAScript变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是最简单的数据,引用值(reference value)则是由多个值构成的对象。
在把一个值赋给变量时, JavaScript引擎必须确定这个值是原始值还是引用值。
6种原始值:
1.Undefined
2.Null
3.Boolean
4.Number
5.String
6.Symbol。
引用值:
我们常见的引用值就是"对象"
保存原始值的变量是按值(by value )访问的,因为我们操作的就是存储在变量中的实际值。
(这句是真的抽象,按我的理解来,按值访问即在栈中保存的实际的数值)
引用值是保存在内存中的对象。
(这句反而好理解,我们可以把他理解为引用值保存的是一个指针,后面的例子会帮助我们更好理解)
与其他语言不同,JavaScript不允许直接访问内存位置(小东西真别致),
因此也就不能直接操作对象所在的内存空间。
在操作对象时,实际上操作的是对该对象的引用(reference)而非实际的对象本身。
为此,保存引用值的变量是按引用(by reference)访问的。
注意:在很多语言中,字符串是使用对象表示的,因此被认为是引用类型
原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。
不过,在变量保存了这个值之后,可以对这个值做什么,则大有不同。
2.1.对于原始值,不能添加属性,举个例子
let panghu_1 = "panghu";
panghu_1.age =20;
console.log(panghu_1.age);
看图:
添加属性不会报错,但原始值不能拥有属性
2.2.对于引用值而言,可以随时添加、修改和删除其属性和方法
let panghu_2 = new String("big panghu");
panghu_2.age = 20;
console.log(panghu_2.age);
看图:
来看看两种值的类型判断:
原始值和引用值在通过变量复制是也有所不同.
3.1.原始值的复制
在通过变量把一个原始值赋值到另一个变量时,原始值会被复制到新变量的位置.
let a = 1;
let b = a;
console.log(b); //1
(不用上图了吧)
3.2.引用值的复制
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。
区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。
操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来,
(让我们提取重点.复制的值是一个指针,复制后,两个变量指向同一个对象)
让我们用代码尝试去证明:
let panghu_1 = new Object();
let panghu_2 = panghu_1;
panghu_1.age = 100;
console.log(panghu_2.age);
上图:
(看,多么神奇的js)
由此可证明,将panghu_1赋值给panghu_2后,两个变量指向的其实是同一个对象
非常形象的一张图:
经过我们在上面的一段大发现,我们又产生了新的问题
4.1.原始值的的参数传递
function original(a){
a= a+10;
return a;
};
let b = 10;
let c = original(b);
console.log(b);
console.log(c);
看图:
这自然是没什么悬念
4.2.初始值的参数传递
例子4.2.1.
function Reference(a) {
a.age = 20;
};
let b = new Object();
Reference(b);
console.log(b.age);
上图,
这怎么解释呢?
到此为止,有的小伙伴就要说了,
创建新对象,保存在b中,将b作为参数调用方法Reference();
此时,b被赋值给了a,此时,a,b指向同一个对象,于是当我a.age=20后,
b的age属性自然就被设置为"20"了(错误的解释)
(思路非常清晰,然而这是错的)
我们加两行代码试试:
例子4.2.2.
function Reference(a) {
a.age = 20;
a = new Object();
a.age = 1000;
};
let b = new Object();
Reference(b);
console.log(b.age);
(装一下:诶,发生甚么事了,不应该是1000吗)
于是,正确的解释来了:
我们创建了一个对象并把它保存在变量 b 中。
然后,这个对象被传给
Reference()方法,并被复制到参数a中在函数内部,a和b都指向同一个对象结果就是,
即使对象是按值传进函数的,a也会通过引用访问对象。
当函数内部给a设置了name属性时,函数外部的对象也会反映这个变化,因为a指向的对象保存在全局作用域的堆内存上。
这解释了例子4.2.1为什么发生
那例子4.2.2如何解释呢?
当a在函数内部被重写时,它变成了一个指向本地对象的指针
而那个对象在函数执行结束时就被销毁了
所以我们会知道:Js中所有函数的参数都是按值传递的