第一百零五篇:变量的原始值和引用值

时间:2022-11-07 07:14:46

好家伙,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.动态属性

原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。

不过,在变量保存了这个值之后,可以对这个值做什么,则大有不同。

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.复制值

 原始值和引用值在通过变量复制是也有所不同.

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.传递参数(参数的复制)

经过我们在上面的一段大发现,我们又产生了新的问题

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中所有函数的参数都是按值传递的