值类型和引用类型

162 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

前面有篇文章已经介绍了js的语言类型,那是基于js的语言类型来划分的。这篇文章我们就来介绍一下哪些是值类型,哪些是引用类型,以及它们之间是如何区分的?值类型和引用类型是基于JavaScript中变量的类型来进行区分的。也就是说值类型和引用类型是基于js的语言类型的基础上进一步划分的。

值类型

值类型指的是JavaScript的基本数据类型,包括:字符串(string)、数值(number)、布尔(boolean)、null、undefined、代表(symbol)(es6新增的)、bigInt。 这几种类型的具体含义在js的语言这篇文章中已经具体介绍了,这里就不再重复了,感兴趣的可以参考我的另外一篇文章: js的语言类型

引用类型

引用类型指的是JavaScript的复杂数据类型,包括:对象(object)、数组(array)、函数(function)、日期(date)、正则表达式(regExp)、基本包装类型对象、单体内置对象。

1. 对象(object)

object是JavaScript的一种数据类型。用于储存各种键值集合和更复杂的实体。

2. 数组

数组是一种列表对象,它的原型中提供了遍历和修改元素的相关操作。

常见操作

2.1 创建数组

let fruits = ['Apple', 'Banana']

2.2 通过索引访问数组元素

let first = fruits[0]
// Apple

let last = fruits[fruits.length - 1]
// Banana

2.3 遍历数组

fruits.forEach(function(item, index, array) {
  console.log(item, index)
})
// Apple 0
// Banana 1

2.4 添加元素到数组的末尾

let newLength = fruits.push('Orange')
// ["Apple", "Banana", "Orange"]

2.5 删除数组末尾的元素

let last = fruits.pop() // remove Orange (from the end)
// ["Apple", "Banana"]

2.6 添加元素到数组的头部

let newLength = fruits.unshift('Strawberry') // add to the front
// ["Strawberry", "Banana"]

2.7 删除数组头部的元素

let first = fruits.shift() // remove Apple from the front
// ["Banana"]

2.8 找出某个元素在数组中的索引

fruits.push('Mango')
// ["Strawberry", "Banana", "Mango"]

let pos = fruits.indexOf('Banana')
// 1

2.9 通过索引删除某个元素

let removedItem = fruits.splice(pos, 1) // this is how to remove an item

// ["Strawberry", "Mango"]

2.10 从一个索引位置删除多个元素

let vegetables = ['Cabbage', 'Turnip', 'Radish', 'Carrot']
console.log(vegetables)
// ["Cabbage", "Turnip", "Radish", "Carrot"]

let pos = 1
let n = 2

let removedItems = vegetables.splice(pos, n)
console.log(vegetables)
// ["Cabbage", "Carrot"] (the original array is changed)

console.log(removedItems)
// ["Turnip", "Radish"]

2.11 复制一个数组

let shallowCopy = fruits.slice() // this is how to make a copy
// ["Strawberry", "Mango"]
3. 函数

每个 JavaScript 函数实际上都是一个 Function 对象。运行 (function(){}).constructor === Function // true 便可以得到这个结论。

function test () {

}
4. 日期

Date 对象则基于 Unix Time Stamp,即自 1970 年 1 月 1 日(UTC)起经过的毫秒数。

创建日期的方式:

new Date();
new Date(value);
new Date(dateString);
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);

参数:

4.1 没有参数

如果没有提供参数,那么新创建的 Date 对象表示实例化时刻的日期和时间。

4.2 Unix时间戳

  • value:一个 Unix 时间戳(Unix Time Stamp),它是一个整数值,表示自 1970 年 1 月 1 日 00:00:00 UTC(the Unix epoch)以来的毫秒数,忽略了闰秒。请注意大多数 Unix 时间戳功能仅精确到最接近的秒。
  • 时间戳字符串 dateString: 表示日期的字符串值。该字符串应该能被 Date.parse() 正确方法识别(即符合 IETF-compliant RFC 2822 timestamps 或 version of ISO8601)。

4.3 分别提供日期与时间的每一个成员

当至少提供了年份与月份时,这一形式的 Date() 返回的 Date 对象中的每一个成员都来自下列参数。没有提供的成员将使用最小可能值(对日期为1,其他为0)。

  • year:表示年份的整数值。0 到 99 会被映射至 1900 年至 1999 年,其它值代表实际年份。参见 示例。
  • monthIndex:表示月份的整数值,从 0(1 月)到 11(12 月)。
  • date可选:表示一个月中的第几天的整数值,从 1 开始。默认值为 1。
  • hours 可选:表示一天中的小时数的整数值 (24 小时制)。默认值为 0(午夜)。
  • minutes 可选:表示一个完整时间(如 01:10:00)中的分钟部分的整数值。默认值为 0。
  • seconds 可选:表示一个完整时间(如 01:10:00)中的秒部分的整数值。默认值为 0。
  • milliseconds 可选:表示一个完整时间的毫秒部分的整数值。默认值为 0。
5. 正则表达式

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExp 的 exec 和 test 方法,以及 String 的 match、matchAll、replace、search 和 split 方法。

6. 基本包装类型

基本包装类型通俗地理解就是js的基本类型所对应的引用类型(对象)。除了null和undefined外,其他的基本类型都有对应的包装对象。

  • string --- String
  • number --- Number
  • bigInt --- BigInt
  • boolean --- Boolean
  • symbol --- Symbol

我们在基本类型变量上调用方法的时候,其实是调用的包装类型的对象上的方法。过程如下:

  1. 根据对象保存的基本类型值,创建对应类型的实例对象
  2. 在这个实例上调用我们所指定的方法
  3. 立即销毁这个实例
7. 单体内置对象

由ECMAScript 实现提供的、不依赖于宿主环境的对象。

单体内置对象主要有两种:Global对象和Math对象

7.1 Global对象

7.1.1 编码

let url = 'http://www.baidu.com';
console.log(encodeURI(url)); // 'http://www.baidu.com'
console.log(encodeURIComponent(url)); // 'http%3A%2F%2Fwww.baidu.com'

7.1.2 解码

let url = 'http%3A%2F%2Fwww.baidu.com';
console.log(decodeURI(url)); // 'http://www.baidu.com'
console.log(decodeURIComponent(url))  // 'http://www.baidu.com'

7.1.3 eval()

eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。

eval(new String("2 + 2")); // 返回了包含"2 + 2"的字符串对象
eval("2 + 2");             // returns 4

建议不要使用

7.1.4 isNaN()

isNaN() 函数用来确定一个值是否为NaN

isNaN(NaN);       // true
isNaN(undefined); // true
isNaN({});        // true

isNaN(true);      // false
isNaN(null);      // false
isNaN(37);        // false

// strings
isNaN("37");      // false: 可以被转换成数值 37
isNaN("37.37");   // false: 可以被转换成数值 37.37
isNaN("37,5");    // true
isNaN('123ABC');  // true:  parseInt("123ABC") 的结果是 123,但是 Number("123ABC") 结果是 NaN
isNaN("");        // false: 空字符串被转换成 0
isNaN(" ");       // false: 包含空格的字符串被转换成 0

// dates
isNaN(new Date());                // false
isNaN(new Date().toString());     // true

isNaN("blabla")   // true: "blabla"不能转换成数值
                  // 转换成数值失败,返回 NaN

7.1.5 isFinite()

isFinite() 函数用来判断被传入的参数值是否为一个有限数值(finite number)

isFinite(Infinity);  // false
isFinite(NaN);       // false
isFinite(-Infinity); // false

isFinite(0);         // true
isFinite(2e64);      // true,在更强壮的 Number.isFinite(null) 中将会得到 false


isFinite("0");       // true,在更强壮的 Number.isFinite('0') 中将会得到 false

7.1.6 parseInt()

parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。

parseInt("0xF", 16);   // 15
parseInt("F", 16);    // 15
parseInt("17", 8);    // 15
parseInt(021, 8);    // 15
parseInt("015", 10);       // 15 parseInt(015, 8); 返回 13
parseInt(15.99, 10);    // 15
parseInt("15,123", 10);    // 15
parseInt("FXX123", 16);    // 15
parseInt("1111", 2);    // 15
parseInt("15 * 3", 10);    // 15
parseInt("15e2", 10);    // 15
parseInt("15px", 10);    // 15
parseInt("12", 13);    // 15

7.1.7 parseFloat()

parseFloat() 函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。

parseFloat(3.14);  // 3.14
parseFloat('3.14');  // 3.14
parseFloat('  3.14  ');  // 3.14
parseFloat('314e-2');  // 3.14
parseFloat('0.0314E+2');  // 3.14
parseFloat('3.14some non-digit characters');  // 3.14
parseFloat({ toString: function() { return "3.14" } });  // 3.14

7.2 Math对象

Math 是一个内置对象,它拥有一些数学常数属性和数学函数方法。 Math 用于 Number 类型 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。引用圆周率的写法是 Math.PI,调用正余弦函数的写法是 Math.sin(x),x 是要传入的参数。Math 的常量是使用 JavaScript 中的全精度浮点数来定义的。

特别说明:

由于篇幅原因,上面这些引用都没有展开详细介绍。后续我会分别根据这些引用对象输出详细的文章,敬请期待哦。

值类型和引用类型之间的区别

  1. 在内存上存储的地方不一样,值类型存在栈中,引用类型存在堆中。
  2. 在传递值类型和传递引用类型的时候,传递的方式不一样。值类型我们称之为值传递,引用类型我们称之为引用传递。