Published on

JavaScript 变量与数据类型

Authors
  • avatar
    Name
    青雲
    Twitter

JavaScript 是一门动态类型语言,这意味着你无需提前声明变量的类型。在程序运行时,解释器会自动处理变量的类型。

变量简介

在 JavaScript 中,变量是用来存储数据值的容器。你可以使用 var, let, 或者 const 来声明一个变量。

  • var:在ES6之前广泛使用,定义一个函数作用域的变量或者定义一个全局变量,无块作用域概念。
  • let:ES6引入,定义一个块级作用域变量,只在最近的一组大括号内有效。
  • const:ES6引入,定义一个块级作用域的常量,并且这个常量不可以再赋值。

示例:

var oldWay = "我是老式声明变量的方式";
let newWay = "我是新式声明变量的方式";
const constantValue = "我是一个常量";

console.log(oldWay); // 输出: 我是老式声明变量的方式
console.log(newWay); // 输出: 我是新式声明变量的方式
console.log(constantValue); // 输出: 我是一个常量

数据类型

基本数据类型(原始类型)

JavaScript 有七种基本数据类型,它们是不可变的和可被复制的。

String(字符串)

代表文本数据,可以包括字母、数字或符号,必须用引号(单引号 '、双引号 " 或模板字符串 ```)包裹。

let greeting = 'Hello, World!';
let favNum = '42';
let template = `I said: ${greeting}`;

Number(数字)

JavaScript 里的所有数字都是浮点类型的,即它们都有小数点。 JavaScript也能表示整数,但这些实际上是没有小数点的浮点数。

let integer = 100;
let floatingPoint = 10.5;

Boolean(布尔值)

只有两个值:truefalse。这是 JavaScript 中最简单的数据类型,经常用于逻辑判断。

let isAdult = true;
let isMinor = false;

Null(空值)

null 在 JavaScript 中表示“无的值”或“空值”。它只有一个值:null

let empty = null;

Undefined(未定义)

一个没有被赋值的变量会有一个默认值 undefined

let notAssigned;
console.log(notAssigned); // 输出 "undefined"

Symbol(符号)

ES6 新增的类型,用于创建匿名和唯一的值。

let sym1 = Symbol('desc');
let sym2 = Symbol('desc');
console.log(sym1 === sym2); // 输出 "false",即使描述相同,也是不同的值

BigInt

ES10 引入的新型数据类型,表示大整数。

const bigInt = 1234567890123456789012345678901234567890n;

let bigNumber = BigInt(9007199254740991) + BigInt(1);
console.log(bigNumber);  // 输出:9007199254740992n

对象类型

在 JavaScript 中,对象可以被看作是属性的集合。与基本类型不同的是,对象类型存储的是引用地址,所以当你复制或者传递时,操作的是同一内存地址。除了基本数据类型以外,JavaScript 中的其他值都是对象。

Object

对象是包含一系列“键值对”的集合。可以用大括号 来创建一个新对象。

let person = {
  name: 'Alice',
  age: 25,
  isStudent: true
};

Array

数组是一种特殊的对象,用于保存一系列有序的值。使用方括号 [] 可以创建一个数组。

let colors = ['Red', 'Green', 'Blue'];
console.log(colors[0]); // "Red"

Function

函数实际上是具有相关联代码的对象。

function greet(name) {
  return 'Hello ' + name;
}
console.log(greet('Alice')); // "Hello Alice"

动态类型

JavaScript 是一种动态类型语言,这意味着同一个变量可以被赋予不同类型的值。 示例:

let dynamicVar = "Hello, World";
console.log(typeof dynamicVar); // 输出: string

dynamicVar = 50;
console.log(typeof dynamicVar); // 输出: number

dynamicVar = true;
console.log(typeof dynamicVar); // 输出: boolean

dynamicVar = function() { console.log("Hi!"); };
console.log(typeof dynamicVar); // 输出: function

类型转换

在 JavaScript 中,类型转换发生在当运算符和函数被应用到不同类型的值上时。类型转换主要分为两种:显式转换和隐式转换。

显示转换

显式类型转换,又称为类型强制,是指开发者直接指定变量从一种类型转换为另一种类型。在 JavaScript 中可以通过各种内建函数进行显式转换。

字符串转换

我们可以使用 String 函数或者 .toString() 方法把一个值转换为字符串。

let value = 123;
let stringValue = String(value); // 使用String函数
console.log(stringValue); // 输出:"123"
console.log(typeof stringValue); // 输出:"string"

stringValue = value.toString(); // 使用.toString()方法
console.log(stringValue); // 输出:"123"
console.log(typeof stringValue); // 输出:"string"

数字转换

要将一个值转换为数字,可以使用 Number 函数,或者 parseIntparseFloat 函数,取决于我们想要整数还是浮点数。

let stringNumber = "123";
let numberValue = Number(stringNumber); // 使用Number函数
console.log(numberValue); // 输出:123
console.log(typeof numberValue); // 输出:"number"

stringNumber = "123.456";
numberValue = parseFloat(stringNumber); // 获取浮点数
console.log(numberValue); // 输出:123.456

numberValue = parseInt(stringNumber); // 获取整数
console.log(numberValue); // 输出:123

布尔转换

Boolean 函数用来把任何类型的值转换为布尔类型。在 JavaScript 中,false, 0, -0, 0n, "", null, undefined, 和 NaN 都是被认为是 falsy(即它们会被转换为 false),而其他所有的值都被当作是 truthy(会被转换为 true)。

let truthyValue = "Hello";
let falsyValue = "";

console.log(Boolean(truthyValue)); // 输出:true
console.log(Boolean(falsyValue)); // 输出:false

隐式类型转换

隐式转换是指当代码中的操作符应用于不同类型的值时,JavaScript 解释器自动将数据类型转换为适合该操作符的类型。这种现象常常会在比较操作、逻辑操作、加法或减法等场合出现。

字符串拼接

使用加号 (+) 来处理字符串和其他类型的值时,JavaScript 会将所有的值转换为字符串。

let result = "Hit points: " + 50; // 数字被转换为字符串
console.log(result); // 输出:"Hit points: 50"

result = "The answer is " + true; // 布尔值被转换为字符串
console.log(result); // 输出:"The answer is true"

数学运算

在执行数学运算时,如果其中一个操作数是字符串,JavaScript 会尝试将这个字符串转换成数字。如果转换失败,则结果为 NaN(Not-a-Number)。

let result = "4" - "2"; // 字符串被转换为数字
console.log(result); // 输出:2,因为两个字符串都可以转换为数字

result = "five" * 2; // 字符串不能被转换为数字
console.log(result); // 输出:NaN

逻辑运算

在逻辑运算如 &&|| 中,JavaScript 会将值转换为布尔类型来决定运算的结果。

let result = "Cat" && "Dog"; // 两个值都是 truthy
console.log(result); // 输出:"Dog"

result = false || "Dog"; // 第一个值是 falsy,返回第二个值
console.log(result); // 输出:"Dog"

比较运算

比较运算中的类型转换尤其有趣,因为不同的比较运算符有不同的行为。 使用 ==(宽松相等)会进行隐式类型转换。

console.log(123 == "123"); // 输出:true,字符串被转换为数字
console.log(1 == true); // 输出:true,布尔值被转换为数字

而使用 ===(严格相等)不会进行类型转换。

console.log(123 === "123"); // 输出:false,没有类型转换
console.log(1 === true); // 输出:false,没有类型转换

类型转换中的陷阱

比较相等时使用 == 而不是 ===

使用 ==(宽松相等)来比较不同类型的值会引发隐式类型转换,而 ===(严格相等)则不会进行类型转换。

// 这会导致隐式类型转换
console.log(0 == "");     // 输出:true
console.log(0 == "0");    // 输出:true
console.log(false == "0"); // 输出:true

// 严格相等运算符不会进行类型转换
console.log(0 === "");    // 输出:false
console.log(0 === "0");   // 输出:false
console.log(false === "0");// 输出:false

如果使用 + 运算符连接字符串和数字

字符串和数字相加时,数字会被转换为字符串,这可能会导致不是你期望的结果。

console.log("3" + 4 + 5); // 输出:"345"
console.log(3 + 4 + "5"); // 输出:"75"

nullundefined 的比较

nullundefined 相比较时是一个特例,使用 == 时它们相等,但是与其他值比较时不是这样。

console.log(null == undefined); // 输出:true
console.log(null == 0);         // 输出:false
console.log(null === undefined);// 输出:false

使用 Object 作为键名

对象在转换为字符串时通常会变成 "[object Object]",因此在使用对象作为字典的键名时要小心。

let obj = {};
obj[{}] = "Value";
console.log(obj["[object Object]"]); // 输出:"Value"

NaN 的奇特行为

NaN 是一个表示非数字的特殊值,它与任何值都不相等,包括它自己。

console.log(NaN == NaN); // 输出:false
console.log(NaN === NaN); // 输出:false
console.log(isNaN(NaN));  // 输出:true

NaN 表示“不是一个数字”,但它的类型是 number

let result = 'abc' - 1;
console.log(result);  // 输出:NaN
console.log(typeof result);  // 输出:number

布尔值的转换规则

console.log(Boolean(0));  // 输出:false
console.log(Boolean(1));  // 输出:true
console.log(Boolean(''));  // 输出:false
console.log(Boolean('Hello'));  // 输出:true
console.log(Boolean(null));  // 输出:false
console.log(Boolean(undefined));  // 输出:false
console.log(Boolean([]));  // 输出:true
console.log(Boolean({}));  // 输出:true

严格模式与非严格模式

JavaScript 可以在严格模式下执行,这可以防止某些不安全的操作,如不声明变量就使用它。 开启严格模式很简单,只需在脚本或者函数的开头加上 use strict

'use strict';
let safeMode = "Now I'm in strict mode";

在严格模式下,如果试图使用未声明的变量,JavaScript 会抛出错误。

面试实战

题一:JavaScript中有哪些数据类型?

答案:JavaScript有两大类数据类型:原始数据类型和对象类型。 原始数据类型包括:

  • undefined:表示未定义,变量声明但未初始化时的值。
  • null:表示空值或不存在的对象引用。
  • boolean:表示逻辑实体,可以是true或false。
  • number:表示数字,可以是整数或浮点数,还包括特殊的NaN和Infinity。
  • string:表示文本数据,字符串序列。
  • symbol:ECMAScript 6引入的新类型,表示独一无二的值,常用于对象属性的键。
  • bigint:ECMAScript 2020引入的新数据类型,用于表示大整数。

对象类型(非原始数据类型)包括:

  • Function:表示代码块,可以执行。
  • Object:表示键值对集合,包括如数组(Array)、日期(Date)和正则表达式(RegExp)等特殊对象类型。

题二:解释var, let和const的区别,并举例说明。

答案var, let, 和 const 是JavaScript中声明变量的三个关键字,它们有不同的作用域和特性。

  • var:声明一个变量,作用域为声明所在的函数或全局作用域,可以重复声明同一个变量,可以在声明之前使用(会提升),变量的值可以被修改。
var name = "Alice";
var name; // 重复声明是允许的
console.log(name); // 输出:Alice
  • let:声明一个块级作用域的变量,不能在同一个块级作用域内重复声明,不能在声明之前使用(不会提升),变量的值可以修改。
let age = 30;
// let age; // SyntaxError: Identifier 'age' has already been declared
{
  let age = 25; // 块级作用域内可以声明
  console.log(age); // 输出:25
}
console.log(age); // 输出:30
  • const:声明一个块级作用域的常量,同样不能重复声明且不能在声明前使用,声明时必须初始化,一旦赋值后不能再被修改(但如果值是对象,则对象的属性可以修改)。
const PI = 3.14;
// PI = 3; // TypeError: Assignment to constant variable.
const obj = { key: "value" };
obj.key = "new value"; // 可以修改对象属性
console.log(obj.key); // 输出:new value

解释以下代码的行为

const a = {name: 'Alice'};
a = {name: 'Bob'};

答案:代码会抛出 TypeError: Assignment to constant variable. 错误。 const 声明的变量是一个常量,不能被重新赋值。这里尝试为 a 重新赋值一个新对象 {name: 'Bob'},因此会抛出类型错误。不过要注意的是,常量对象的属性是可以修改的。

const a = { name: 'Alice' };
a.name = 'Bob';
console.log(a.name);  // 输出 'Bob'

题三:如何检测变量的数据类型?

答案:可以使用typeof运算符或instanceof运算符,还可以使用Object.prototype.toString.call()方法。

  • typeof: 对于原始数据类型和函数比较有效。
typeof "Hello"; // "string"
typeof 42;      // "number"
typeof true;    // "boolean"
typeof Symbol(); // "symbol"
typeof undefined; // "undefined"
typeof function() {}; // "function"
  • instanceof:用于检查一个对象是否为一个特定的构造函数的实例。
[] instanceof Array;        // true
({}) instanceof Object;    // true
  • Object.prototype.toString.call():可以得到对象类型的最佳方式,对于所有标准的对象和用户定义的对象都有效。
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"

题四:说明null和undefined的区别

  • null是JavaScript的字面量,表示空值;它是一个对象类型,故意指定为"没有对象",这表示该处不应该有值。
  • undefined是全局对象的属性,表示变量已声明但还未被初始化。

一个变量在定义后没有赋值,那么它的值就是undefined。而如果你想表示某个值或变量目前不存在,应该用null来表示。

let foo;
console.log(foo); // undefined

let bar = null;
console.log(bar); // null

解释以下代码的输出

console.log(typeof null);
console.log(typeof undefined);

答案:

  • typeof null 输出 'object'null 是一个特殊的关键字,表示一个空对象引用。在 JavaScript 的最初版本中,typeof null 也被定义为了 'object',这是一个早期实现中的错误,但出于兼容性考虑,这个错误被保留下来了。
  • typeof undefined 输出 'undefined'undefined 表示变量已声明但未初始化,所以 typeof undefined 正常返回 'undefined'

题五:解释以下代码的输出

let x = 10;
let y = '10';

console.log(x + y);
console.log(x - y);

答案

  • x + y 输出 '1010'。在 x + y 中,+ 操作符对一个字符串和一个数字进行操作时,进行字符串拼接,因此结果为 '1010'
  • x - y 输出 0。在 x - y 中,- 操作符会将字符串 '10' 转换为 10,于是计算结果为数值 0

题六:== 和 === 有什么不同?

答案== 是等值比较运算符,执行相等测试时会进行类型转换。如果两个值不是同一类型,JavaScript 会尝试将它们转换为一个适当的类型来比较。 === 是严格等值比较运算符,不进行类型转换。如果两个值类型不一致,=== 会立即返回 false,而不会试图将它们转换为同一类型。

解释以下代码的输出

let a;
let b = null;
console.log(a == b);
console.log(a === b);

答案

  • a == b 输出 true。== 运算符在比较时,会将 undefinednull 看作相等,因此 a == b 输出 true
  • a === b 输出 false=== 是严格相等运算符,它不仅比较值还比较类型。aundefinedbnull,类型不同,因此 a === b 输出 false