TS-引用类型

1.定义

TS中的数据值分为原始值和引用值两种类型。原始值即最简单的数据,而引用值则是有多个值构成的复合对象。

原始值和引用值的声明方式类似,都需要创建常量或变量,然后对其赋值。其不同之处是,在变量或常量保存之后,可以对这个值进行的操作有所区别。

2.原始值与引用值

原始值:存储在栈中(stack)中的数据,它们的值直接存储在变量的存储空间中。

引用值:存储在堆(heap)中的对象,存储在变量中的值是一个指针,它指向实际存储对象的内存地址。

布尔类型、数值类型、长整型和字符串类型等原始类型,它们的值即原始值。这些原始类型占据的空间通常是固定的,所以可将它们存储在较小的内存区域——栈中,便于迅速查询变量的值。

引用类型通常是由多个原始值组成的复合对象类型,这些类型(数组、函数、对象与类等)将在后面一一介绍。对于引用类型的值,由于它们的大小并不固定,且通常较大,因此不能把它们放在栈中,否则会降低变量查询的速度。栈中只存放了对象在堆中的地址,而对象实际存放在堆中。

 

 3.值的复制

对于原始值,赋值时会在栈中产生一个新的副本,因此复制的值和原来的值之间没有任何联系,它们各自位于不同的栈区。

let number1 = 7;
let number2 = number1; //将number1的值复制到number2
let bool1 = true;
let bool2 = bool1;     //将bool1的值复制到bool2

 当发生修改时,各变量的栈区独立变化,互不干扰。例如,在以下代码中,对变量number1和bool1的操作不会影响number2和bool2的值。

number1 = 8;
console.log(number2); // 输出7
bool1 = false;
console.log(bool2);   //输出true

对于引用值,在赋值时会赋予变量对象的引用(即对象的存储地址),而并非对象本身,因此复制时变量复制了相同的引用地址。例如,以下代码分别创建了名为object1、object2的两个字面量对象和名为array1、array2的两个数组

let object1 = { property1: 1 };
let object2 = object1; //将object1的引用地址复制到object2
let array1 = ["a", "b", "c"]
let array2 = array1; //将array1的引用地址复制到array2 

 由于多个变量实际上引用了同一个对象,因此对该对象的修改会在其他相关引用中体现出来

object1.property1 = 2;
console.log(object2);  //输出{ property1: 2 }
array1[1] = "x";
console.log(array2); //输出["a", "x", "c"]

引用值的对象修改后在堆和栈中的存储方式如图

 

但如果重新给引用变量赋新值,引用发生改变,指向另外的堆地址,变量和原有对象不再有任何关系,两者之间互不影响。 

object2 = { property1: 3 };
array2 = ["x", "y", "z"]
console.log(object1); //输出{ property1: 2 }
console.log(array1);  //输出["a", "x", "c"]

引用值重新赋值后在堆和栈中的存储方式如图

 

4.值的传递

值的传递和值的复制具有相似的规则。对于原始值,复制各自独立的副本;而对于引用值,复制相同的引用地址。
当把原始值传递给函数的参数时,参数是全新的副本。在函数中修改参数值,并不会影响原来的值。 

let number1 = 7;
function testNumber(para: number) {
    para = 8;
}
testNumber(number1);
console.log(number1);//输出7
 
let bool1 = true;
function testBool(para: boolean) {
    bool1 = false;
}
testBool(bool1);
console.log(bool1); //输出true

当把引用值传递给函数时,传递给函数的是对原值的引用,在函数内部可以使用此引用来修改对象本身的值。代码如下。 

let object1 = { property1: 1 };
function testObject(para: any) {
    para.property1 = 2;
}
testObject(object1);
console.log(object1); //输出{ property1: 2 }
 
let array1 = ["a", "b", "c"]
function testArray(para: string[]) {
    para[1] = "x";
}
testArray(array1);
console.log(array1); //输出["a", "x", "c"]

 如果给函数参数赋予新值,引用就会发生改变,指向另外的堆地址,参数和原有对象不再有任何关系,两者之间互不影响。

function testObject2(para: any) {
    para = { property1: 3 };
}
testObject2(object1);
console.log(object1); //输出{ property1: 2 }
 
function testArray2(para: string[]) {
    para = ["x", "y", "z"];
}
testArray(array1);
console.log(array1); //输出["a", "x", "c"]

5.值的比较

 当对原始值进行比较时,会逐字节地比较,以判断它们是否相等。注意,比较的是值本身,而不是值所处的栈的位置。当比较结果为相等时,表示它们在栈中所包含的字节信息是相同的。

let number1 = 7;
let number2 = 7;
//number1 和number2的值具有相同的字节信息,比较结果为相等,输出true
console.log(number1 == number2); 
 
let bool1 = true;
let bool2 = true;
//bool1和bool2的值具有相同的字节信息,比较结果为相等,输出true
console.log(number1 == number2); 

原始值的比较方式如图 

 

当对引用值进行比较时,比较的是两个引用地址,看它们引用的是否是同一个对象,而不是比较它们的字节信息是否相同。即使两个引用值引用的对象具有相同的字节信息,如果引用的堆地址不同,它们也不是相等的。 

 

let object1 = { property1: 1 };
let object2 = { property1: 1 };
//object1和object2指向不同的对象地址,因此不相等,以下语句输出false
console.log(object1 == object2); 
let object3 = object1;
//object1和object3均指向同一个对象地址,因此相等,以下语句输出true
console.log(object1 == object3); 
object1.property1 = 5;
console.log(object1 == object3); //输出true
 
let array1 = ["a", "b", "c"];
let array2 = ["a", "b", "c"];

//array1 和array2 指向不同的对象地址,因此不相等,
//以下语句输出false
console.log(array1 == array2); 
let array3 = array1;
//array1 和array3均指向同一个对象地址,因此相等,
//以下语句输出true
console.log(array1 == array3); 
array1[1] = "x";
console.log(array1 == array3); //输出true

引用值的比较方式如图

 

6.常量的使用

 使用const关键字声明常量,而常量的值是不可改变的。例如,在以下代码中,修改常量的值会引起编译错误。

const number1 = 7;
const bool1 = true;
//编译错误:无法分配到 "number1" ,因为它是常数。ts(2588)
number1 = 8;
//编译错误:无法分配到 "bool1" ,因为它是常数。ts(2588)
bool1 = false;

然而,严格来说,常量仅能限定栈上的内容不可编辑,但堆上的内容可以编辑。例如,以下代码不会引起编译错误。 

const object1 = { property1: 1 };
const array1 = ["a", "b", "c"]
object1.property1 = 2;
array1[1] = "x";

但如果更改栈上的引用地址,就会引起编译错误,示例代码如下。

const object1 = { property1: 1 };
const array1 = ["a", "b", "c"];
// 编译错误:无法分配到 "object1" ,因为它是常数。ts(2588)
object1 = { property1: 1 };
// 编译错误:无法分配到 "array1" ,因为它是常数。ts(2588)
array1 = ["a", "b", "c"];

 虽然const关键字限定了栈上的内容不可编辑,但堆上的内容可以编辑,因此对于引用类型来说,要使堆上的内容不可编辑,需要额外使用readonly关键字

  • 35
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值