变量类型

在脚本中,无论是属性、数据库还是临时变量,每个数据都会以类型进行区分。
例如,在数据库中,可以存储数字、文本、真假值。
在这个过程中,可能会涉及到类型的比较与转换。
var 数字变量 = 1
var 文本变量 = '一个文本'
var 真假值变量 = true
var 真假值变量2 = 2 > 1 // true
我们称数字、文本、真假值、undefined、null为标量类型。因为它们之间,每个值是唯一的。例如,1会和1相等,false会和false相等,诸如此类。
其中一个例外是 NaN,NaN 是数字类型,表示该数字是无效的。NaN 总是不等于 NaN,需要使用 isNaN(值)来判断一个值是否为 NaN。
var 数字1 = 123
var 数字2 = 123
数字1 === 数字2 // true

var 无效数字 = NaN
无效数字 === NaN // false
isNaN(无效数字) // true
与标量类型相对应的,是对象和数组类型。这两个类型是按引用传递的,根据引用来比较两个值是否相等。例如,如果声明两个空数组,它们之间是不相等的。
var 数组1 = []
var 数组2 = []
数组1 === 数组2 // false

数字

数字(number)类型是所有变量中最常见的类型。
可以对数字进行加减乘除的运算。
1 + 2 // 返回 3
2 * 6 // 返回 12
+5 // 返回 5
可以使用Number(数字)将其它值强制转换为数字。如果被转换的值无法转换成数字,则会被转换成NaNNaN 的全称为 “Not a Number”,表示这个值不是一个合法数字。即便如此,它依然是数字类型。
Number("123") // 返回 123
Number("123") === 123 // true
Number('123.') // 返回 123

Number(true) // 1
Number(false) // 0

Number("123d") // NaN
Number("ABC") // NaN
Number(undefined) // NaN
如前文所述,NaN 总是不等于 NaN,需要使用 isNaN(值)来判断一个值是否为 NaN。
var 无效数字 = NaN
无效数字 === NaN // false
isNaN(无效数字) // true
数字类型包括整数和小数。
255 === 255.0 // true
值得注意的是,在 JavaScript 中,由于小数在内部通过特殊方法存储,导致在对小数计算时,会存在误差。因此,会出现不符合直觉的情况,例如:
0.3 === 0.1 + 0.2 // false
0.1 + 0.2 // 0.30000000000000004
因此,在对小数进行运算和处理时,要小心谨慎。特别是将数字转换成文本后,小数可能会产生非常长的一段文本。
String(0.1 + 0.2) // '0.30000000000000004'
这时,可以通过 .toFixed() 对小数长度进行截取。
var 小数变量 = 12345.6789;

小数变量.toFixed(); // 返回 "12346"
小数变量.toFixed(1); // 返回 "12345.7"
小数变量.toFixed(2); // 返回 "12345.78"
也可以使用 Math.floor(小数)Math.ceil(小数)Math.round(小数)对小数进行向下取整、向上取整和四舍五入。
Math.floor(1.7) // 返回 1,向下取整到 1
Math.ceil(1.7) // 返回 2,向上取整到 2
Math.round(1.7) // 返回 2,四舍五入到 2
此外,JavaScript 的整数也是有精度限制的。当数字超过9007199254740991时(包括取负号),会丢失精度。

文本

文本(string),同样是 JavaScript 中非常常用的类型。可以使用单引号'、双引号"和反引号`来表示。
var 文本变量1 = '这是一个文本'
var 文本变量2 = "这也是一个文本"
var 文本变量3 = `这还是一个文本`
如果文本中需要包含引号,可以使用反斜线\进行转义。
var 包含引号的文本 = "这是一个包含\"引号\"的文本"
使用 \n 可以让文本换行。
var 包含换行的文本 = '段落1\n段落2'
文本类型也可以进行拼接。可以使用加号+,将两个文本拼接成一个文本。
var 文本1 = 'Hello, '
var 文本2 = 'world!'
var 拼接后的文本 = 文本1 + 文本2 // 'Hello, world!'
当使用反引号时,可以使用${变量}在文本中插入变量。
var 文本1 = `你的金钱数量:${属性.金钱}`
var 文本2 = `打 7 折后的价钱为:${(100*0.7).toFixed(2)}`
使用 .length 获取文本的长度。
var 文本变量 = '这是一个文本'
文本变量.length // 返回 7
使用 文本[位置] 获取文本中指定位置的字符。位置从 0 开始计数。
var 文本变量 = '这是一个文本'
文本变量[1] // 返回 '是'
使用 .slice(开始位置, 结束位置) 截取文本的一部分。
var 文本变量 = '这是一个文本'
文本变量.slice(2, 4) // 返回 '一个'
使用 String(xxx) 将其它类型强制转换为文本。
String(123) // 返回 '123'
String(true) // 返回 'true'
String(undefined) // 返回 'undefined'
String(null) // 返回 'null'
使用 .indexOf().lastIndexOf() 查找文本中指定子文本的位置。如果找不到,则返回 -1。
var 文本变量 = '这是一个文本,是吗?'
文本变量.indexOf('是') // 返回 1,“是”第一次出现的位置
文本变量.lastIndexOf('是') // 返回 8,“是”最后一次出现的位置
使用 .toUpperCase().toLowerCase() 将文本中的字母转换成大写或小写。
var 文本变量 = 'Hello World!'
文本变量.toUpperCase() // 返回 'HELLO WORLD!'
文本变量.toLowerCase() // 返回 'hello world!'
使用 .replaceAll() 将文本中的子文本全部替换成另一个子文本。
var 文本变量 = '这是一个文本'
文本变量.replaceAll('是', '不是') // 返回 '这不是一个文本'
使用 .split(分割文本) 将文本分割成一个数组。
'这是一个文本'.split('') // ['这', '是', '一', '个', '文', '本']
'这是一个文本'.split('是') // ['这', '一个文本']
'a,b,c,d,e,f'.split(',') // ['a', 'b', 'c', 'd', 'e', 'f']
使用 .trim() 去除文本的两端的空白字符。
var 文本变量 = ' 这是一个文本 '
var 去除空白后的文本 = 文本变量.trim() // '这是一个文本'

真假值

真假值(boolean),也称布尔值。包括 truefalse 两个值。
使用感叹号,可以将真假值取反。
!true // false
!!true // true (取反两次,先变成 false 再变成 true)
可以通过由比较符号(==, ===,>=,<=,<,>)组成的表达式来生成真假值,例如:
var 真假值变量 = 属性.金钱 > 100
也可以通过 Boolean(xxx)将其它类型强制转换为真假值。如果被转换的值是 0、空字符串''、undefined、null,则转换后的值为 false。其它的值,包括负数、空数组[]、空对象{},都会被转换为 true。
Boolean(0) // false
Boolean(1) // true
Boolean('') // false
If 会根据括号内的真假值,决定是否要执行花括号里的命令。如果括号里是别的类型,则先强制转换为真假值,再进行判断(相当于在外面套了层 Boolean(xxx))。
if (真假值) {

}

比较符号 == 和 ===

在 JavaScript 中,有两种方式来比较两个值是否相等:=====
== 是一种宽松的相等检查。当使用 == 比较两个值时,如果它们的类型不同,JavaScript 会尝试将它们转换为相同的类型,然后再进行比较。例如:
123 == '123' // true
true == 1 // true
undefined == null // true
在上述例子中,尽管比较的两个值的类型不同,但是 == 还是返回了 true。这是因为 == 在比较前进行了类型转换。
== 不同,=== 是一种严格的相等检查。当使用 === 比较两个值时,如果它们的类型不同,=== 就会直接返回 false,不会进行任何类型转换。例如:
123 === '123' // false
true === 1 // false
undefined === null // false
在编写 JavaScript 代码时,你应当尽可能的使用 === 进行比较,从而避免由于类型转换带来的一些意想不到的结果。

undefined 和 null

在 JavaScript 中,undefinednull 是两种特殊的类型。

undefined

当你声明了一个变量但还没有赋值,那么它的值就是 undefined。也可以明确地将变量赋值为 undefined
var 未定义变量;
未定义变量 // undefined

var 变量2 = undefined;
变量2 // undefined
如果你尝试获取一个数组、对象和数据库中不存在的值,也会返回 undefined
var 对象 = {};
对象.不存在的属性 // undefined

var 数组 = [];
数组[0] // undefined

var 变量 = 数据.(['不存在', '的', '路径'])
变量 // undefined

null

null 通常用于表示变量的值故意被置为空。
var 空变量 = null;

?? 操作符

?? 操作符用于判断一个值是否为 undefinednull,如果是,则返回另一个值,否则返回这个值本身。
var 变量 = undefined ?? '默认值';
变量 // '默认值'

var 变量2 = null ?? '默认值';
变量2 // '默认值'

var 变量3 = '已定义的值' ?? '默认值';
变量3 // '已定义的值'

转换为其他类型

undefinednull 转换为其他类型时,也有一些特殊的规则。
转换为布尔值时,undefinednull 都会转换为 false
Boolean(undefined) // false
Boolean(null) // false
在转换为字符串时,undefined 转换为 "undefined",而 null 转换为 "null"
String(undefined) // "undefined"
String(null) // "null"
当转换为数字时,undefined 转换为 NaN,而 null 转换为 0
Number(undefined) // NaN
Number(null) // 0
在实际开发中,undefinednull 都表示变量没有值。通常情况下,我们会把变量初始化为 null,表示变量已经被初始化但目前还没有值。而 undefined 通常表示系统级的、出乎意料的或定义自己的缺少的,尚未赋值的情况。

数组

数组(array)是一种特殊的对象,用于表示和操作一组有序的值。
在 JavaScript 中,使用方括号 [] 来创建一个数组。例如:
var 数组变量 = ['元素1', '元素2', '元素3'];
在这个例子中,我们创建了一个数组,它有三个元素:'元素1''元素2''元素3'
通过索引(从0开始的整数)来访问数组的元素:
数组变量[0] // '元素1'
通过赋值操作符 = 来修改数组的元素:
数组变量[0] = '新元素';
数组变量[0] // '新元素'
使用 length 属性来获取数组的长度:
数组变量.length // 3
使用 push 来向数组的末尾添加新的元素:
数组变量.push('新元素');
数组变量.length // 4
数组变量[3] // '新元素'
使用 pop 来删除并返回数组的最后一个元素:
数组变量.pop() // '新元素'
数组变量.length // 3
使用 shift 来删除并返回数组的第一个元素:
数组变量.shift() // '新元素'
数组变量.length // 2
使用 unshift 来向数组的开头添加新的元素:
数组变量.unshift('新元素');
数组变量.length // 3
数组变量[0] // '新元素'
使用 indexOf 来查找数组中某个元素的索引:
数组变量.indexOf('元素2') // 1
数组变量.indexOf('不存在的元素') // -1
使用 slice 来获取数组的一部分:
数组变量.slice(1, 3) // ['元素2', '元素3']
使用 splice 来删除、替换或添加数组的元素:
数组变量.splice(1, 1, '新元素') // ['元素2']
数组变量 // ['新元素', '元素3']
使用 join 来将数组的元素连接成一个字符串:
数组变量.join(', ') // '新元素, 元素3'
使用 ... 操作符来将数组的元素展开,并合并到新数组中:
var 数组变量 = ['元素1', '元素2', '元素3'];
var 新数组变量 = [...数组变量, '元素4'];
新数组变量 // ['元素1', '元素2', '元素3', '元素4']

对象

对象(object)可以包含多个属性,每个属性都由一个键(key)和一个值(value)组成。对象的键是文本,而值可以是任何类型的数据。
在 JavaScript 中,我们可以使用花括号 {} 来创建一个对象。例如:
var 对象变量 = {1: '值1',2: '值2',3: '值3'
}
这个代码中创建了一个对象,它有三个属性:键1键2键3,它们的值分别是'值1''值2''值3'
通过.操作符来访问对象的属性:
对象变量.1 // '值1'
也可以使用[]操作符来访问对象的属性,特别是当属性名包含特殊字符或者属性名是变量时:
对象变量['键1'] // '值1'
var 属性名 = '键2';
对象变量[属性名] // '值2'
通过赋值操作符=来修改对象的属性,如果不存在则添加该属性:
对象变量.1 = '新值';
对象变量.1 // '新值'
使用delete操作符来删除对象的属性:
delete 对象变量.1;
对象变量.1 // undefined
对象变量 // { 键2: '值2', 键3: '值3' }
使用in操作符来检查对象是否包含某个属性:
'键2' in 对象变量 // true
'不存在的键' in 对象变量 // false
使用Object.keys(对象)来获取对象的所有属性名:
Object.keys(对象变量) // ['键2', '键3', '新键']
使用Object.values(对象)来获取对象的所有属性值:
Object.values(对象变量) // ['值2', '值3', '新值']
使用Object.entries(对象)来获取对象的所有属性名和属性值:
Object.entries(对象变量) // [['键2', '值2'], ['键3', '值3'], ['新键', '新值']]
使用 ... 操作符来将对象展开并合并到新的对象中:
var 对象变量2 = {
  ...对象变量,4: '值4'
}
对象变量2 // { 键2: '值2', 键3: '值3', 新键: '新值', 键4: '值4' }

函数

函数(function)也可以作为变量类型的一种。它可以接收参数,执行一段代码,并返回一个值。
JavaScript 中的函数与 Realm 在领域中提供的“全局函数”在功能上很相似,但是“全局函数”可以被所有脚本调用,而在 JavaScript 中声明的函数只能被当前脚本调用。
在 JavaScript 中,使用 function 关键字来声明一个函数。例如:
function 加法(参数1, 参数2, 参数3) {
  return 参数1 + 参数2 + 参数3; 
}
在这个例子中,我们声明了一个函数 加法,它接收三个参数 参数1参数2参数3,并返回这三个参数的和。
声明函数后,我们可以通过函数名来调用它,并传入实际的参数值:
加法(1, 2, 3); // 返回 6
同时,在函数内的代码,可以访问外部声明的临时变量。
var 手续费率 = 0.01;
function 计算手续费(金额) {
  return 金额 * 手续费率;
}
但是,在函数内声明的临时变量,外界却访问不了:
function 计算手续费(金额) {
  var 手续费率 = 0.01;
  return 金额 * 手续费率;
}
手续费率; // 这里访问不到 手续费率 临时变量
函数也可以直接赋值给变量:
var 变量1 = function(参数1, 参数2, 参数3) {
  return 参数1 + 参数2 + 参数3; 
}

变量1(1, 2, 3); // 返回 6
在这个例子中,我们声明了一个匿名函数,并将它赋值给了变量 变量1。这个匿名函数和前面的 加法 函数是一样的,只是它没有名字。
因为函数可以作为变量,所以它可以像其它普通的变量一样,存放在数组、对象中,或者作为参数传递给其它函数。
将函数存放在数组中:
var 函数1 = function(参数1, 参数2, 参数3) {
  return 参数1 + 参数2 + 参数3; 
}

var 函数2 = function(参数1, 参数2, 参数3) {
  return 参数1 * 参数2 * 参数3; 
}

var 函数数组 = [函数1, 函数2];

函数数组[0](1, 2, 3); // 返回 6
函数数组[1](1, 2, 3); // 返回 6
将函数存放在对象中:
var 对象变量 = {
  属性1: function(参数1, 参数2, 参数3) {
    return 参数1 + 参数2 + 参数3;
  }
}

对象变量.属性1(1, 2, 3); // 返回 6
此外,当函数处于对象中时,可以使用 this 来访问当前对象的属性。
var 变量1 = {
  名字: '张三',
  年龄: 20,
  打招呼: function() {
    return '你好,我叫' + this.名字;
  },
  庆祝生日: function() {
    this.年龄++;
  }
}

变量1.打招呼(); // 返回 '你好,我叫张三'
变量1.年龄; // 返回 20
变量1.庆祝生日();
变量1.年龄; // 返回 21
在这个例子中,变量1 对象有两个属性 名字年龄,还有两个函数 打招呼庆祝生日打招呼 函数会返回一个问候语文本,庆祝生日 函数会使 age 属性增加 1。
this 表示当前对象,在 打招呼 函数中,this.名字 就相当于 变量1.名字
在一些复杂情况下,我们可能希望this不被函数影响,这时,会用到箭头函数:
var 对象变量 = {
  属性1: (参数1, 参数2, 参数3) => {
    // 这里面如果有 this 不会指向函数所在的对象
    return 参数1 + 参数2 + 参数3;
  }
}
(参数) => {} 相当于 function () {},如果在函数中没有用到 this,箭头函数就是普通函数的另一种写法。
函数也可以作为参数或返回值。当函数作为参数时,我们称这个函数为回调函数。例如:
function 函数1(回调函数) {
  return 回调函数();
}

函数1(function () {
  return 'Hello, world!';
}); // 返回 'Hello, world!'
在这里,函数1 接收一个回调函数作为参数,并在函数内部调用了这个回调函数。

typeof 操作符

在 JavaScript 中,typeof 可以用来检查一个变量的类型。
typeof 操作符后面跟着一个变量或值,它会返回一个字符串,表示该变量或值的类型。例如:
typeof 123 // "number"
typeof 'Hello' // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object"
typeof {} // "object"
typeof [] // "object"
typeof function() {} // "function"
需要注意的是,对于 null 和数组,typeof 返回的是 "object"
在实际编程中,typeof 是一个非常有用的工具,它可以帮助我们在处理变量时,更好地理解变量的类型,从而避免一些类型相关的错误。你可以对 typeof 的返回值进行“提示”。
提示(typeof 临时变量)

变量与 JSON 之间的转换

JSON 是一种将 JavaScript 对象或数组转换为文本的方法。

JSON.stringify()

JSON.stringify() 可以将对象或数组转换为文本。
var 对象变量 = {
  名称: "张三",
  年龄: 30,
  爱好: ["阅读", "旅行"]
};

var 文本变量 = JSON.stringify(对象变量); // '{"名称":"张三","年龄":30,"爱好":["阅读","旅行"]}'

JSON.parse()

JSON.stringify() 相对的是 JSON.parse() ,该命令将 JSON 文本还原成对象或数组。
var 文本变量 = '{"名称":"张三","年龄":30,"爱好":["阅读","旅行"]}';
var 对象变量 = JSON.parse(文本变量);
对象变量.名称; // "张三"
对象变量.爱好[0]; // "阅读"

注意事项

如果想要将一个对象或数组转换为 JSON,请确保该对象或数组只含有数字、文本和真假值。在处理其它特殊变量(如 NaN、函数、Date等)时,可能会出现意料意料之外的情况。
例如,JSON 会将 NaN 转换为 null,并忽略 undefined 和函数值。
var 对象 = {
  名称: "张三",
  介绍: undefined,
  打招呼: function() {
    return `你好,我是${this.名称}`;
  }
};
JSON.stringify(对象); // '{"名称":"张三"}',undefined 和函数被忽略