Skip to content

变量与数据类型

变量是存储数据的容器,数据类型则定义了数据的种类。本章将详细介绍 JavaScript 中的变量声明方式和各种数据类型。

变量声明

JavaScript 中有三种声明变量的方式:varletconst

var 关键字

var 是 JavaScript 最早期的变量声明方式:

javascript
// 使用 var 声明变量
var name;           // 声明变量,未赋值
var age = 25;       // 声明并赋值

// var 声明的变量可以重复声明
var name = '张三';
var name = '李四';   // 不会报错,会覆盖之前的值
console.log(name);  // 输出:李四

// var 声明的变量有变量提升
console.log(city);  // 输出:undefined(不会报错)
var city = '北京';  // 变量提升:声明被提升到作用域顶部
// 等价于:
// var city;
// console.log(city);
// city = '北京';

// var 是函数作用域
function test() {
    if (true) {
        var message = 'Hello';  // message 在整个函数内都有效
    }
    console.log(message);  // 输出:Hello
}
test();
// console.log(message);  // 错误:message 未定义(函数外无法访问)

// var 声明的变量会成为全局对象的属性
var globalVar = '全局变量';
console.log(window.globalVar);  // 输出:全局变量(浏览器环境)

let 关键字

let 是 ES6 新增的声明方式,解决了 var 的一些问题:

javascript
// 使用 let 声明变量
let name = '张三';
let age = 25;

// let 声明的变量不能重复声明
let city = '北京';
// let city = '上海';  // 错误:不能重复声明

// let 没有变量提升
// console.log(x);  // 错误:Cannot access 'x' before initialization
let x = 10;

// let 是块级作用域
if (true) {
    let blockVar = '块级变量';
    console.log(blockVar);  // 输出:块级变量
}
// console.log(blockVar);  // 错误:blockVar 未定义

// let 在循环中的应用
for (let i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);  // 输出:0, 1, 2(每次循环都是新的 i)
    }, 100);
}

// 对比 var
for (var j = 0; j < 3; j++) {
    setTimeout(function() {
        console.log(j);  // 输出:3, 3, 3(同一个 j)
    }, 100);
}

const 关键字

const 用于声明常量,声明后不能重新赋值:

javascript
// 使用 const 声明常量
const PI = 3.14159;
const MAX_SIZE = 100;

// const 声明时必须初始化
// const x;  // 错误:缺少初始化

// const 声明的变量不能重新赋值
const name = '张三';
// name = '李四';  // 错误:不能重新赋值

// const 也是块级作用域
if (true) {
    const temp = '临时常量';
    console.log(temp);  // 输出:临时常量
}
// console.log(temp);  // 错误:temp 未定义

// 注意:const 声明的对象,其属性可以修改
const person = {
    name: '张三',
    age: 25
};
person.name = '李四';  // 允许修改属性
person.city = '北京';  // 允许添加属性
console.log(person);   // 输出:{name: '李四', age: 25, city: '北京'}

// person = {};  // 错误:不能重新赋值整个对象

// const 声明的数组,元素可以修改
const colors = ['红', '绿', '蓝'];
colors.push('黄');     // 允许添加元素
colors[0] = '橙色';    // 允许修改元素
console.log(colors);   // 输出:['橙色', '绿', '蓝', '黄']

// colors = [];  // 错误:不能重新赋值整个数组

三种声明方式的区别

javascript
// 对比表格
/*
| 特性           | var          | let          | const        |
|----------------|--------------|--------------|--------------|
| 作用域         | 函数作用域    | 块级作用域    | 块级作用域    |
| 变量提升       | 是           | 否           | 否           |
| 重复声明       | 允许         | 不允许       | 不允许       |
| 重新赋值       | 允许         | 允许         | 不允许       |
| 全局对象属性   | 是           | 否           | 否           |
| 暂时性死区     | 否           | 是           | 是           |
*/

// 最佳实践:
// 1. 默认使用 const
// 2. 需要重新赋值时使用 let
// 3. 避免使用 var

// 示例
const API_URL = 'https://api.example.com';  // 常量用 const
let count = 0;  // 需要变化的值用 let
count++;

数据类型

JavaScript 有 8 种数据类型,分为原始类型和引用类型。

原始类型(基本类型)

原始类型的值是不可变的,直接存储在栈内存中。

Number 数字类型

javascript
// 整数
let intNum = 42;
let negativeNum = -10;

// 浮点数
let floatNum = 3.14;
let floatNum2 = 0.1;
let floatNum3 = .5;  // 等同于 0.5

// 科学计数法
let scientificNum = 2.5e6;  // 2500000
let smallNum = 2.5e-3;      // 0.0025

// 二进制(ES6)
let binaryNum = 0b1010;  // 10

// 八进制
let octalNum = 0o12;     // 10(ES6)
let octalNum2 = 012;     // 10(严格模式不支持)

// 十六进制
let hexNum = 0xFF;       // 255

// 特殊数值
let infinity = Infinity;       // 正无穷
let negInfinity = -Infinity;   // 负无穷
let notANumber = NaN;          // 非数字

// 检测特殊数值
console.log(isFinite(100));     // true
console.log(isFinite(Infinity)); // false
console.log(isNaN(NaN));        // true
console.log(isNaN('hello'));    // true(会尝试转换为数字)
console.log(Number.isNaN('hello')); // false(更严格的判断)

// 浮点数精度问题
console.log(0.1 + 0.2);         // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

// 解决精度问题
function add(num1, num2) {
    // 转换为整数计算,再转回小数
    const precision = 10;
    return (num1 * precision + num2 * precision) / precision;
}
console.log(add(0.1, 0.2));  // 0.3

// 数值范围
console.log(Number.MAX_VALUE);          // 最大数值
console.log(Number.MIN_VALUE);          // 最小正数值
console.log(Number.MAX_SAFE_INTEGER);   // 最大安全整数(9007199254740991)
console.log(Number.MIN_SAFE_INTEGER);   // 最小安全整数

String 字符串类型

javascript
// 使用单引号
let singleQuote = 'Hello';

// 使用双引号
let doubleQuote = "World";

// 使用模板字符串(ES6)
let template = `Hello, World!`;

// 字符串拼接
let greeting = 'Hello' + ', ' + 'World!';
console.log(greeting);  // Hello, World!

// 使用模板字符串拼接
let name = '张三';
let age = 25;
let info = `姓名:${name},年龄:${age}`;
console.log(info);  // 姓名:张三,年龄:25

// 模板字符串可以包含表达式
let a = 10;
let b = 20;
console.log(`${a} + ${b} = ${a + b}`);  // 10 + 20 = 30

// 模板字符串可以换行
let html = `
    <div class="container">
        <h1>标题</h1>
        <p>内容</p>
    </div>
`;

// 转义字符
let escaped = 'He said, "Hello!"';  // 单引号中包含双引号
let escaped2 = "It's a test";       // 双引号中包含单引号
let escaped3 = 'It\'s a test';      // 使用反斜杠转义
let newline = '第一行\n第二行';      // \n 换行
let tab = '列1\t列2';               // \t 制表符
let backslash = '路径:C:\\Users';   // \\ 反斜杠

// 字符串长度
let str = 'Hello';
console.log(str.length);  // 5

// 中文字符
let chinese = '你好';
console.log(chinese.length);  // 2

// 访问字符
console.log(str[0]);        // H
console.log(str.charAt(0)); // H
console.log(str[str.length - 1]);  // o(最后一个字符)

// 字符串是不可变的
let s = 'Hello';
// s[0] = 'h';  // 错误:不能修改
s = 'hello';    // 只能重新赋值

Boolean 布尔类型

javascript
// 布尔值只有两个值
let isTrue = true;
let isFalse = false;

// 比较运算返回布尔值
console.log(5 > 3);   // true
console.log(5 < 3);   // false
console.log(5 === 5); // true
console.log(5 !== 3); // true

// 逻辑运算返回布尔值
console.log(true && false);  // false
console.log(true || false);  // true
console.log(!true);          // false

// 条件语句中使用
let age = 18;
if (age >= 18) {
    console.log('已成年');
}

// 布尔转换
console.log(Boolean(1));       // true
console.log(Boolean(0));       // false
console.log(Boolean(''));      // false
console.log(Boolean('text'));  // true
console.log(Boolean(null));    // false
console.log(Boolean(undefined)); // false

Undefined 类型

javascript
// undefined 表示变量已声明但未赋值
let x;
console.log(x);          // undefined
console.log(typeof x);   // undefined

// 函数没有返回值时返回 undefined
function noReturn() {
    // 没有返回语句
}
console.log(noReturn());  // undefined

// 访问不存在的属性返回 undefined
let obj = { name: '张三' };
console.log(obj.age);  // undefined

// 数组越界访问返回 undefined
let arr = [1, 2, 3];
console.log(arr[10]);  // undefined

// 判断 undefined
let value;
if (value === undefined) {
    console.log('value 是 undefined');
}

// 注意:undefined 不是保留字,可以被重新赋值(不推荐)
// undefined = 'test';  // 不要这样做

Null 类型

javascript
// null 表示"空"或"无"
let empty = null;
console.log(empty);         // null
console.log(typeof empty);  // object(历史遗留问题)

// null 和 undefined 的区别
// null:表示"没有值",是主动设置的
// undefined:表示"缺少值",是默认状态

let person = null;  // 明确表示没有对象
let person2;        // 还未赋值

// 判断 null
if (empty === null) {
    console.log('empty 是 null');
}

// null 和 undefined 比较
console.log(null == undefined);   // true(宽松相等)
console.log(null === undefined);  // false(严格相等)

// 转换为数字
console.log(Number(null));       // 0
console.log(Number(undefined));  // NaN

// 初始化对象时使用 null
let user = null;  // 初始化为 null,后续会赋值对象
user = { name: '张三' };

Symbol 类型(ES6)

javascript
// Symbol 是 ES6 新增的原始类型,表示唯一的标识符
let sym1 = Symbol();
let sym2 = Symbol('description');  // 可选的描述字符串

// 每个 Symbol 都是唯一的
let sym3 = Symbol('test');
let sym4 = Symbol('test');
console.log(sym3 === sym4);  // false(即使描述相同也不相等)

// Symbol 作为对象属性键(保证属性名唯一)
let id = Symbol('id');
let user = {
    name: '张三',
    [id]: 12345  // 使用计算属性名
};
console.log(user[id]);  // 12345

// Symbol 属性不会出现在 for...in 循环中
for (let key in user) {
    console.log(key);  // 只输出 name
}

// 获取 Symbol 属性
console.log(Object.getOwnPropertySymbols(user));  // [Symbol(id)]

// Symbol.for() 创建全局 Symbol
let globalSym1 = Symbol.for('app.id');
let globalSym2 = Symbol.for('app.id');
console.log(globalSym1 === globalSym2);  // true(同一个全局 Symbol)

// 获取 Symbol 的描述
console.log(sym2.description);  // description

BigInt 类型(ES2020)

javascript
// BigInt 用于表示任意大的整数
// 在数字后面加 n 或使用 BigInt() 函数

// 使用 n 后缀
let bigNum1 = 9007199254740991n;
let bigNum2 = 123456789012345678901234567890n;

// 使用 BigInt() 函数
let bigNum3 = BigInt(9007199254740991);
let bigNum4 = BigInt('123456789012345678901234567890');

// BigInt 运算
console.log(bigNum1 + 1n);   // 9007199254740992n
console.log(bigNum1 * 2n);   // 18014398509481982n

// BigInt 和 Number 不能混合运算
// console.log(bigNum1 + 1);  // 错误

// 需要显式转换
console.log(bigNum1 + BigInt(1));  // 正确
console.log(Number(bigNum1) + 1);  // 正确(但可能丢失精度)

// 比较运算
console.log(1n === 1);   // false(类型不同)
console.log(1n == 1);    // true(宽松相等)
console.log(1n < 2);     // true

// typeof 检测
console.log(typeof bigNum1);  // bigint

引用类型

引用类型的值是可变的,存储在堆内存中,变量保存的是引用地址。

Object 对象

javascript
// 创建对象
let person = {
    name: '张三',
    age: 25,
    city: '北京'
};

// 访问属性
console.log(person.name);      // 张三
console.log(person['age']);    // 25

// 修改属性
person.age = 26;
person['city'] = '上海';

// 添加新属性
person.job = '工程师';

// 删除属性
delete person.city;

// 检查属性是否存在
console.log('name' in person);           // true
console.log(person.hasOwnProperty('name'));  // true

// 遍历对象
for (let key in person) {
    console.log(key + ': ' + person[key]);
}

// 对象方法
let calculator = {
    value: 0,
    add: function(num) {
        this.value += num;
        return this;
    },
    subtract: function(num) {
        this.value -= num;
        return this;
    },
    getValue: function() {
        return this.value;
    }
};

calculator.add(10).subtract(3);
console.log(calculator.getValue());  // 7

Array 数组

javascript
// 创建数组
let arr1 = [1, 2, 3, 4, 5];
let arr2 = new Array(1, 2, 3);
let arr3 = new Array(5);  // 创建长度为 5 的空数组

// 访问元素
console.log(arr1[0]);     // 1
console.log(arr1[arr1.length - 1]);  // 5

// 修改元素
arr1[0] = 10;

// 数组长度
console.log(arr1.length);  // 5

// 添加元素
arr1.push(6);        // 末尾添加
arr1.unshift(0);     // 开头添加

// 删除元素
arr1.pop();          // 删除末尾元素
arr1.shift();        // 删除开头元素

// 数组可以包含不同类型的元素
let mixed = [1, 'hello', true, null, { name: '张三' }, [1, 2, 3]];

Function 函数

javascript
// 函数声明
function greet(name) {
    return 'Hello, ' + name + '!';
}

// 函数表达式
let greet2 = function(name) {
    return 'Hello, ' + name + '!';
};

// 箭头函数(ES6)
let greet3 = (name) => 'Hello, ' + name + '!';

// 调用函数
console.log(greet('张三'));  // Hello, 张三!

// 函数是对象
console.log(typeof greet);  // function
console.log(greet instanceof Object);  // true

类型检测

typeof 操作符

javascript
// typeof 返回类型的字符串
console.log(typeof 42);           // number
console.log(typeof 3.14);         // number
console.log(typeof 'hello');      // string
console.log(typeof true);         // boolean
console.log(typeof undefined);    // undefined
console.log(typeof null);         // object(历史遗留问题)
console.log(typeof {});           // object
console.log(typeof []);           // object
console.log(typeof function(){}); // function
console.log(typeof Symbol());     // symbol
console.log(typeof 10n);          // bigint

// typeof 的局限性
// 1. null 返回 object
// 2. 数组返回 object
// 3. 无法区分不同对象类型

instanceof 操作符

javascript
// instanceof 检测对象是否是某个构造函数的实例
let arr = [1, 2, 3];
console.log(arr instanceof Array);   // true
console.log(arr instanceof Object);  // true

let obj = {};
console.log(obj instanceof Object);  // true
console.log(obj instanceof Array);   // false

let date = new Date();
console.log(date instanceof Date);   // true

// instanceof 的局限性
// 只能用于对象,不能用于原始类型
console.log(42 instanceof Number);   // false
console.log('hi' instanceof String); // false

更精确的类型检测

javascript
// 使用 Object.prototype.toString
function getType(value) {
    return Object.prototype.toString.call(value).slice(8, -1);
}

console.log(getType(42));           // Number
console.log(getType('hello'));      // String
console.log(getType(true));         // Boolean
console.log(getType(null));         // Null
console.log(getType(undefined));    // Undefined
console.log(getType({}));           // Object
console.log(getType([]));           // Array
console.log(getType(function(){})); // Function
console.log(getType(new Date()));   // Date
console.log(getType(/regex/));      // RegExp

// 使用 Array.isArray() 检测数组
console.log(Array.isArray([1, 2, 3]));  // true
console.log(Array.isArray({}));         // false

// 检测 NaN
console.log(Number.isNaN(NaN));       // true
console.log(Number.isNaN('hello'));   // false

// 检测有限数字
console.log(Number.isFinite(100));       // true
console.log(Number.isFinite(Infinity));  // false
console.log(Number.isFinite('100'));     // false

类型转换

转换为字符串

javascript
// String() 函数
console.log(String(123));        // '123'
console.log(String(true));       // 'true'
console.log(String(null));       // 'null'
console.log(String(undefined));  // 'undefined'
console.log(String([1, 2, 3]));  // '1,2,3'
console.log(String({}));         // '[object Object]'

// toString() 方法
console.log((123).toString());     // '123'
console.log(true.toString());      // 'true'
// console.log(null.toString());   // 错误:null 没有 toString 方法
// console.log(undefined.toString()); // 错误

// 字符串拼接
console.log(123 + '');           // '123'
console.log(true + '');          // 'true'

// 模板字符串
console.log(`${123}`);           // '123'
console.log(`${true}`);          // 'true'

转换为数字

javascript
// Number() 函数
console.log(Number('123'));      // 123
console.log(Number('123.5'));    // 123.5
console.log(Number(''));         // 0
console.log(Number('abc'));      // NaN
console.log(Number(true));       // 1
console.log(Number(false));      // 0
console.log(Number(null));       // 0
console.log(Number(undefined));  // NaN

// parseInt() 解析整数
console.log(parseInt('123'));      // 123
console.log(parseInt('123.9'));    // 123(只取整数部分)
console.log(parseInt('123abc'));   // 123(解析到非数字停止)
console.log(parseInt('abc123'));   // NaN
console.log(parseInt('10', 2));    // 2(二进制转十进制)
console.log(parseInt('FF', 16));   // 255(十六进制转十进制)

// parseFloat() 解析浮点数
console.log(parseFloat('3.14'));     // 3.14
console.log(parseFloat('3.14abc'));  // 3.14
console.log(parseFloat('3'));        // 3

// 一元加号运算符
console.log(+'123');    // 123
console.log(+'3.14');   // 3.14
console.log(+'abc');    // NaN

// 隐式转换(算术运算)
console.log('123' - 0);    // 123
console.log('123' * 1);    // 123
console.log('123' / 1);    // 123

转换为布尔值

javascript
// Boolean() 函数
console.log(Boolean(1));         // true
console.log(Boolean(0));         // false
console.log(Boolean(-1));        // true
console.log(Boolean(''));        // false
console.log(Boolean('text'));    // true
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN));       // false
console.log(Boolean([]));        // true
console.log(Boolean({}));        // true

// 假值(Falsy Values)
// 以下值转换为布尔值时为 false:
// false, 0, -0, 0n, '', null, undefined, NaN

// 真值(Truthy Values)
// 除了假值以外的所有值都是真值

// 双重否定
console.log(!!1);      // true
console.log(!!0);      // false
console.log(!!'text'); // true
console.log(!!'');     // false

// 条件语句中的隐式转换
let value = 'hello';
if (value) {
    console.log('value 是真值');
}

对象转换

javascript
// 对象转换为原始值
let obj = {
    value: 10,
    // valueOf() 方法:返回对象的原始值
    valueOf() {
        return this.value;
    },
    // toString() 方法:返回对象的字符串表示
    toString() {
        return `value: ${this.value}`;
    }
};

console.log(obj + 5);     // 15(调用 valueOf)
console.log(String(obj)); // value: 10(调用 toString)

// 数组转换为字符串
let arr = [1, 2, 3];
console.log(String(arr));  // '1,2,3'
console.log(arr + '');     // '1,2,3'

// 日期转换为字符串
let date = new Date();
console.log(String(date));  // 'Fri Mar 20 2026 ...'
console.log(Number(date));  // 时间戳(毫秒数)

小结

本章学习了 JavaScript 的变量和数据类型:

  • 变量声明:var(函数作用域)、let(块级作用域)、const(常量)
  • 原始类型:Number、String、Boolean、Undefined、Null、Symbol、BigInt
  • 引用类型:Object、Array、Function
  • 类型检测:typeof、instanceof、Object.prototype.toString
  • 类型转换:转换为字符串、数字、布尔值的方法

下一章我们将学习 运算符,了解 JavaScript 中的各种运算操作。