编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

    • 基础入门

      • README
      • 入门介绍
      • 数据类型
        • 2.1 数据类型概述
          • 2.1.1 类型分类
          • 2.1.2 对象类型
          • 2.1.3 数据类型差异
          • 2.1.4 变量常量
          • 2.1.5 基于原型
        • 2.2 原始类型
          • 2.2.1 number
          • 2.2.2 string
          • 2.2.3 boolean
          • 2.2.4 undefined
          • 2.2.5 null
          • 2.2.6 symbol
          • 2.2.7 bigint
        • 2.3 引用类型
          • 2.3.1 object
          • 2.3.2 array
          • 2.3.3 function
          • 2.3.4 date
          • 2.3.5 regexp
          • 2.3.6 map
          • 2.3.7 set
        • 2.4 类型检测
          • 2.4.1 typeof
          • 2.4.2 instanceof
          • 2.4.3 Object.prototype.toString
        • 2.5 对象
          • 2.5.1 对象概述
          • 2.5.2 对象定义
          • 2.5.3 访问对象属性
          • 2.5.4 修改对象属性
          • 2.5.5 添加新属性
          • 2.5.6 删除属性
          • 2.5.7 对象方法
          • 2.5.8 this关键字
          • 2.5.9 对象遍历
          • 2.5.10 对象解构
          • 2.5.11 对象合并
          • 2.5.12 对象原型
          • 2.5.13 对象冻结
          • 2.5.14 属性描述符与Object.defineProperty
        • 2.6 类型转换
          • 2.6.1 显式转换
          • 2.6.2 隐式转换
          • 2.6.3 类型转换的完整规则表
      • 运算符
      • 函数
      • 面向对象
      • 标准库
      • 异步操作
      • 事件设计
      • 错误机制
      • 模块开发
      • 字符串处理
      • 迭代器与生成器
      • Symbol
      • DOM操作
      • 网络请求
    • 综合案例

    • 专栏博客

  • CodeX
  • JavaScript入门
  • 基础入门
杨充
2025-08-26
目录

数据类型

# 目录介绍

  • 2.1 数据类型概述
    • 2.1.1 类型分类
    • 2.1.2 对象类型
    • 2.1.3 数据类型差异
    • 2.1.4 变量常量
    • 2.1.5 基于原型
  • 2.2 原始类型
    • 2.2.1 number
    • 2.2.2 string
    • 2.2.3 boolean
    • 2.2.4 undefined
    • 2.2.5 null
    • 2.2.6 symbol
    • 2.2.7 bigint
  • 2.3 引用类型
    • 2.3.1 object
    • 2.3.2 array
    • 2.3.3 function
    • 2.3.4 date
    • 2.3.5 regexp
    • 2.3.6 map
    • 2.3.7 set
  • 2.4 类型检测
    • 2.4.1 typeof
    • 2.4.2 instanceof
    • 2.4.3 Object.prototype.toString
  • 2.5 对象
    • 2.5.1 对象概述
    • 2.5.2 对象定义
    • 2.5.3 访问对象属性
    • 2.5.4 修改对象属性
    • 2.5.5 添加新属性
    • 2.5.6 删除属性
    • 2.5.7 对象方法
    • 2.5.8 this关键字
    • 2.5.9 对象遍历
    • 2.5.10 对象解构
    • 2.5.11 对象合并
    • 2.5.12 对象原型
    • 2.5.13 对象冻结
  • 2.6 类型转换
    • 2.6.1 显式转换
    • 2.6.2 隐式转换

# 2.1 数据类型概述

JavaScript 是一种动态类型语言,变量的数据类型在运行时确定。JavaScript 提供了多种数据类型,分为原始类型(Primitive Types)和引用类型(Reference Types)。

# 2.1.1 类型分类

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。

  • 1.字符串(string):文本(比如Hello World)。
  • 2.数值(number):整数和小数(比如1和3.14)。
  • 3.布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)。
  • 4.undefined型:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值。
  • 5.null型:表示空值(Null),即此处的值为空。
  • 6.对象(object):各种值组成的集合。

通常,数值、字符串、布尔值这三种类型,合称为原始类型(primitive type)的值,即它们是最基本的数据类型,不能再细分了。对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。至于undefined和null,一般将它们看成两个特殊值。

# 2.1.2 对象类型

对象是最复杂的数据类型,又可以分成三个子类型。

  • 狭义的对象(object)
  • 数组(array)
  • 函数(function)

狭义的对象和数组是两种不同的数据组合方式,除非特别声明,本教程的“对象”都特指狭义的对象。函数其实是处理数据的方法,JavaScript 把它当成一种数据类型,可以赋值给变量,这为编程带来了很大的灵活性,也为 JavaScript 的“函数式编程”奠定了基础。

# 2.1.3 数据类型差异

JavaScript 和 Java 在数据类型方面的差异,不仅仅是两者所采用的术语不同。

  • JavaScript:JavaScript是一种动态类型语言,变量的数据类型在运行时确定。1.不需要显式声明变量类型;2.类型检查在运行时进行。
  • Java:Java是一种静态类型语言,变量的数据类型在编译时确定。1.必须显式声明变量类型;2.类型检查在编译时进行。

像 Java 这样,变量具有数据类型的语言,被称为静态数据类型语言;而像 JavaScript 这样,变量没有类型的语言,则被称为动态数据类型语言。

# 2.1.4 变量常量

变量是可以在程序运行过程中被重新赋值的标识符。在 JavaScript 中,变量可以通过 var、let 或 const 声明。

1.var

  • 作用域:函数作用域(function-scoped)。
  • 提升:变量声明会被提升到函数或全局作用域的顶部。
  • 重新声明:允许在同一作用域内重新声明。
var x = 10;
if (true) {
    var x = 20; // 重新声明,覆盖外部的 x
}
console.log(x); // 输出: 20
1
2
3
4
5

2.let

  • 作用域:块级作用域(block-scoped)。
  • 提升:变量声明会被提升,但在声明之前访问会抛出 ReferenceError(暂时性死区)。
  • 重新声明:不允许在同一作用域内重新声明。
let y = 10;
if (true) {
    let y = 20; // 新的块级作用域,不影响外部的 y
}
console.log(y); // 输出: 10
1
2
3
4
5

常量是声明后不能被重新赋值的标识符。在 JavaScript 中,常量通过 const 声明。

  • 作用域:块级作用域(block-scoped)。
  • 提升:常量声明会被提升,但在声明之前访问会抛出 ReferenceError(暂时性死区)。
  • 重新赋值:不允许重新赋值。
  • 重新声明:不允许在同一作用域内重新声明。
  • 注意:const 声明的常量如果是对象或数组,其属性或元素可以被修改。
const z = 10;
// z = 20; // 报错: Assignment to constant variable

const obj = { name: 'Alice' };
obj.name = 'Bob'; // 允许修改属性
console.log(obj.name); // 输出: Bob
1
2
3
4
5
6

# 2.1.5 基于原型

基于类(Class-based)

类是对象的蓝图,定义了对象的属性和方法。对象是类的实例,通过 new 关键字创建。

强调封装、继承和多态。类是静态的,对象是动态的。典型的语言:Java、C++、Python。

基于原型

对象直接从其他对象继承属性和方法,而不是通过类。每个对象都有一个原型(prototype),通过原型链实现继承。对象可以动态修改,甚至可以在运行时添加或删除属性和方法。

没有类的概念,对象本身就是模板。典型的语言:JavaScript。

JavaScript 选择基于原型的设计,主要基于以下原因:

  1. JavaScript 最初是为浏览器设计的脚本语言,需要处理动态的、不可预测的用户交互。基于原型的模型允许对象在运行时动态修改,更适合这种场景。
  2. 基于原型的模型比基于类的模型更简单,不需要引入类的概念。
  3. JavaScript 支持函数式编程,函数是一等公民。基于原型的模型与函数式编程的结合更加自然,例如通过闭包实现封装。

JavaScript 中的类(ES6)

尽管 JavaScript 是基于原型的语言,但 ES6 引入了 class 语法糖,使得开发者可以使用类似基于类的语法。

注意:ES6 的 class 本质上是基于原型的语法糖,底层仍然使用原型链。

# 2.2 原始类型

原始类型是直接存储在栈内存中的简单数据,按值传递。

原始类型与引用类型的内存原理:JavaScript 引擎使用栈内存(Stack) 和堆内存(Heap) 两种存储方式。原始类型(number、string、boolean 等)的值直接存储在栈中,赋值时进行值拷贝;引用类型(object、array、function 等)的实际数据存储在堆中,栈中只保存一个指向堆内存的引用(指针)。因此,当你将一个对象赋值给另一个变量时,两个变量指向的是同一个堆内存地址,修改其中一个会影响另一个。这也是为什么需要"深拷贝"的原因。

# 2.2.1 number

Number 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。

let age = 25;
let price = 99.99;
1
2

number类型的底层原理:JavaScript 的 number 遵循 IEEE 754 双精度浮点数标准,使用64位存储:1位符号位 + 11位指数位 + 52位尾数位。这意味着:

  • 最大安全整数为 Number.MAX_SAFE_INTEGER(2^53 - 1 = 9007199254740991)
  • 超过安全范围的整数运算会丢失精度
  • 浮点数运算存在精度问题
// 经典的浮点数精度问题
console.log(0.1 + 0.2);           // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);   // false

// 解决方案1:使用 Number.EPSILON 比较
function floatEqual(a, b) {
    return Math.abs(a - b) < Number.EPSILON;
}
console.log(floatEqual(0.1 + 0.2, 0.3)); // true

// 解决方案2:转为整数运算
console.log((0.1 * 10 + 0.2 * 10) / 10); // 0.3

// 特殊值
console.log(Infinity);          // 正无穷
console.log(-Infinity);         // 负无穷
console.log(NaN);               // Not a Number
console.log(NaN === NaN);       // false(NaN不等于自身!)
console.log(Number.isNaN(NaN)); // true(推荐的判断方式)
console.log(isNaN('hello'));    // true(全局isNaN会先转换类型,不推荐)
console.log(Number.isNaN('hello')); // false(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

V8 的 Smi 优化:虽然规范要求所有数字都是双精度浮点数,但 V8 引擎会对小整数(Small Integer, Smi) 做特殊优化——在32位系统中,-2^30 到 2^30-1 范围内的整数直接存储为机器整数(不用分配堆内存),从而大幅提升整数运算性能。

# 2.2.2 string

String用于表示一个字符序列,即字符串。字符串需要使用 单引号 或 双引号 括起来。

let name = "Alice";
let message = 'Hello, world!';
let greeting = `Hello, ${name}!`; // 模板字符串,这个是变量拼接
1
2
3

如果要在单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义。双引号字符串内部使用双引号,也是如此。

'Did she say \'Hello\'?'
// "Did she say 'Hello'?"

"Did she say \"Hello\"?"
// "Did she say "Hello"?"
1
2
3
4
5

# 2.2.3 boolean

表示逻辑值,只有 true 和 false 两个值。

let isActive = true;
let isCompleted = false;
1
2

布尔值使用特殊情况。如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""或''(空字符串)
if ('') {
  console.log('true');
}
// 没有任何输出

if ([]) {
  console.log('true');
}
// true
1
2
3
4
5
6
7
8
9

# 2.2.4 undefined

表示变量已声明但未赋值。undefined 型只能够取 undefined 这一个值。对 undefined 值进行 typeof 运算,其结果为 "undefined"。

let x;
console.log(x); // undefined
1
2

下面总结了会出现 undefined 值的情况。

  • 未初始化的变量的值
  • 不存在的属性的值
  • 在没有传入实参而调用函数时,该函数内相应参数的值
  • 没有 return 语句或是 return 语句中不含表达式的函数的返回值
  • 对 void 运算符求值的结果(常常会通过使用 void 0 来获取一个 undefined 值)

# 2.2.5 null

表示空值或对象不存在。

let y = null;
console.log(y); // null
1
2

对于null和undefined,大致可以像下面这样理解。

  1. null表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入null,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误。
  2. undefined表示“未定义”

null 型没有与之相对应的 Null 类。因此,如果像下面这样对 null 值进行点运算,就会产生 TypeError 异常。

null.toString();
TypeError: null has no properties
1
2

# 2.2.6 symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。通常用于对象属性的键。

const id = Symbol("id");
console.log(id); // Symbol(id)
1
2

注意:遇到唯一性的场景时要想到 Symbol

# 2.2.7 bigint

表示任意精度的整数,用于处理大整数。

const bigNumber = 123456789012345678901234567890n;
console.log(bigNumber); // 123456789012345678901234567890n
1
2

# 2.3 引用类型

引用类型是存储在堆内存中的复杂数据,按引用传递。

# 2.3.1 object

表示键值对的集合,是 JavaScript 中最常用的数据类型。

const person = {
  name: "Alice",
  age: 25,
};
console.log(person.name); // Alice
1
2
3
4
5

# 2.3.2 array

数组定义。表示有序的元素集合,是一种特殊的对象。数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

var arr = ['a', 'b', 'c'];
const numbers = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 1
1
2
3

数组的空位。当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。虽然这个位置没有值,引擎依然认为这个位置是有效的。

var a = [1, , 1];
a.length // 3
1
2

数组的本质。数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。

typeof [1, 2, 3] // "object"
1

# 2.3.3 function

表示可执行的代码块,也是一种对象。

function greet(name) {
  console.log(`Hello, ${name}!`);
}
greet("Alice"); // Hello, Alice!
1
2
3
4

# 2.3.4 date

表示日期和时间。

const now = new Date();
console.log(now); // 当前日期和时间
1
2

# 2.3.5 regexp

表示正则表达式,用于匹配字符串。

const regex = /hello/i;
console.log(regex.test("Hello, world!")); // true
1
2

# 2.3.6 map

表示键值对的集合,键可以是任意类型。

const map = new Map();
map.set("name", "Alice");
console.log(map.get("name")); // Alice
1
2
3

# 2.3.7 set

表示唯一值的集合。

const set = new Set([1, 2, 3, 3, 4]);
console.log(set); // Set { 1, 2, 3, 4 }
1
2

# 2.4 类型检测

JavaScript 有三种方法,可以确定一个值到底是什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法

# 2.4.1 typeof

typeof运算符可以返回一个值的数据类型。检测变量的原始类型。

console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (历史遗留问题)
console.log(typeof Symbol("id")); // "symbol"
console.log(typeof 123n); // "bigint"
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function () {}); // 函数返回 `function`。
1
2
3
4
5
6
7
8
9
10

利用这一点,typeof可以用来检查一个没有声明的变量,而不报错。

v
// ReferenceError: v is not defined

// 变量`v`没有用`var`命令声明,直接使用就会报错。但是,放在`typeof`后面,就不报错了,而是返回`undefined`。
typeof v
// "undefined"
1
2
3
4
5
6

实际编程中,这个特点通常用在判断语句。

// 错误的写法
if (v) {
  // ...
}
// ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
  // ...
}
1
2
3
4
5
6
7
8
9
10

# 2.4.2 instanceof

检测对象是否属于某个引用类型。

console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(function () {} instanceof Function); // true
1
2
3

# 2.4.3 Object.prototype.toString

更精确地检测数据类型。

console.log(Object.prototype.toString.call(42)); // "[object Number]"
console.log(Object.prototype.toString.call("hello")); // "[object String]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
1
2
3

# 2.5 对象

# 2.5.1 对象概述

从底层实现来看,JavaScript 的对象和 Java 的对象在基本原则上是相同的。两者都是内存中的实体,保持着某种状态,并且是用于编程操作的目标对象。但是,从高层概念来看的话,就会发现两者有着不小的差别。

Java 中的对象可以认为是类的一种实例化结果, 而 JavaScript 中并没有类这样的语言构造。

JavaScript 中的对象(Object)是一种复合数据类型,用于存储键值对(key-value pairs)。

对象是 JavaScript 中最核心的概念之一,几乎所有的 JavaScript 实体(如数组、函数等)都是对象或基于对象。

JavaScript 中的对象是一个名称与值配对的集合。这种名称与值的配对被称为属性。这样一来,JavaScript 对象可以定义为属性的集合。

# 2.5.2 对象定义

对象是由一组属性和方法组成的集合。属性是键值对,键是字符串(或 Symbol),值可以是任意数据类型(包括其他对象)。

1.对象字面量。使用花括号 {} 定义对象。

const person = {
  name: "Alice",
  age: 25,
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};
1
2
3
4
5
6
7

2.使用 new Object()。使用构造函数创建对象。

const person = new Object();
person.name = "Alice";
person.age = 25;
person.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};
1
2
3
4
5
6

# 2.5.3 访问对象属性

第一种方式:使用.来访问。

console.log(person.name); // Alice
person.greet(); // Hello, my name is Alice
1
2

第二种方式:使用 [] 来访问。适用于动态属性名或属性名包含特殊字符的情况。

console.log(person["name"]); // Alice
const key = "age";
console.log(person[key]); // 25
1
2
3

# 2.5.4 修改对象属性

直接赋值即可修改属性值。

person.age = 30;
console.log(person.age); // 30
1
2

# 2.5.5 添加新属性

直接赋值即可添加新属性。

person.job = "Developer";
console.log(person.job); // Developer
1
2

# 2.5.6 删除属性

使用 delete 关键字删除属性。

var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);

delete person.name
console.log(person);
1
2
3
4
5
6
7

# 2.5.7 对象方法

对象的方法是一个函数类型的属性。

const person = {
  name: "Alice",
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};
person.greet(); // Hello, my name is Alice
1
2
3
4
5
6
7

# 2.5.8 this关键字

this 指向当前对象,用于访问对象的属性和方法。

const person = {
  name: "Alice",
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};
person.greet(); // Hello, my name is Alice
1
2
3
4
5
6
7

# 2.5.9 对象遍历

1.for...in 循环。遍历对象的可枚举属性。

for (let key in person) {
  console.log(`${key}: ${person[key]}`);
}
1
2
3

2.Object.keys()。返回对象的所有键组成的数组。

const keys = Object.keys(person);
console.log(keys); // ["name", "age", "greet"]
1
2

3.Object.values()。返回对象的所有值组成的数组。

const values = Object.values(person);
console.log(values); // ["Alice", 25, function]
1
2

4.Object.entries()。返回对象的键值对组成的数组。

const entries = Object.entries(person);
console.log(entries); // [["name", "Alice"], ["age", 25], ["greet", function]]
1
2

# 2.5.10 对象解构

从对象中提取属性并赋值给变量。

const { name, age } = person;
console.log(name); // Alice
console.log(age); // 25
1
2
3

# 2.5.11 对象合并

1.Object.assign()。将多个对象合并到一个对象中。

const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 2 }
1
2
3
4

2.扩展运算符(...)

const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2 }
1
2
3
4

# 2.5.12 对象原型

每个对象都有一个原型(prototype),用于继承属性和方法。

const person = {
  name: "Alice",
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const student = Object.create(person);
student.name = "Bob";
student.greet(); // Hello, my name is Bob
1
2
3
4
5
6
7
8
9
10

# 2.5.13 对象冻结

1.Object.freeze()。冻结对象,使其不可修改。

const obj = { a: 1 };
Object.freeze(obj);
obj.a = 2; // 无效
console.log(obj.a); // 1
1
2
3
4

2.Object.seal()。密封对象,使其不能添加或删除属性,但可以修改现有属性。

const obj = { a: 1 };
Object.seal(obj);
obj.a = 2; // 有效
obj.b = 3; // 无效
console.log(obj); // { a: 2 }
1
2
3
4
5

# 2.5.14 属性描述符与Object.defineProperty

JavaScript 对象的每个属性都有一个属性描述符(Property Descriptor),控制属性的行为:

const obj = {};

// 使用 Object.defineProperty 精确控制属性
Object.defineProperty(obj, 'name', {
    value: 'Alice',
    writable: false,      // 不可修改
    enumerable: true,     // 可枚举(for...in 可遍历)
    configurable: false   // 不可删除,不可重新配置
});

obj.name = 'Bob';          // 静默失败(严格模式下会报错)
console.log(obj.name);      // 'Alice'

// getter / setter(访问器属性)
const user = {
    _age: 25,
    get age() {
        return this._age;
    },
    set age(value) {
        if (value < 0 || value > 150) {
            throw new RangeError('年龄必须在 0-150 之间');
        }
        this._age = value;
    }
};

console.log(user.age);    // 25
user.age = 30;             // OK
// user.age = -1;          // RangeError

// 查看属性描述符
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
// { value: 'Alice', writable: false, enumerable: true, configurable: false }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

底层原理:V8 引擎使用隐藏类(Hidden Class / Map) 来优化对象属性访问。当你用对象字面量创建对象时,V8 为其分配一个隐藏类;每次添加新属性,V8 都会创建一个新的隐藏类(通过转换链连接)。如果多个对象以相同顺序添加相同属性,它们共享隐藏类,这使得属性访问可以通过固定偏移量完成(类似 C++ 的结构体),而非哈希表查找。

// 推荐:在构造函数/字面量中一次性声明所有属性
const good = { a: 1, b: 2, c: 3 };  // 一个隐藏类

// 不推荐:动态添加属性(每次添加都生成新的隐藏类)
const bad = {};
bad.a = 1;  // 隐藏类转换
bad.b = 2;  // 隐藏类转换
bad.c = 3;  // 隐藏类转换
1
2
3
4
5
6
7
8

# 2.6 类型转换

# 2.6.1 显式转换

使用函数或方法显式转换类型。

let num = Number("123"); // 字符串转数字
let str = String(123); // 数字转字符串
let bool = Boolean(0); // 数字转布尔值
1
2
3

# 2.6.2 隐式转换

JavaScript 在特定场景下自动转换类型。

隐式转换的底层原理:JavaScript 引擎在执行运算时,会调用内部的抽象操作进行类型转换。+ 运算符遇到字符串时会调用 ToPrimitive() → ToString(),将另一个操作数转为字符串进行拼接;-、*、/ 运算符则调用 ToNumber() 将操作数转为数字。对象转原始类型时,引擎先调用 [Symbol.toPrimitive](hint) 方法,若不存在则依次尝试 valueOf() 和 toString()(hint 为 "number" 时先 valueOf,hint 为 "string" 时先 toString)。理解这一机制,就能解释 [] + [] 为 ""、[] + {} 为 "[object Object]" 等看似奇怪的行为。

let result = "5" + 2; // "52" (字符串拼接)
let sum = "5" - 2; // 3 (字符串转数字)
let isTrue = !0; // true (数字转布尔值)
1
2
3

# 2.6.3 类型转换的完整规则表

原始值 转Number 转String 转Boolean
undefined NaN 'undefined' false
null 0 'null' false
true 1 'true' true
false 0 'false' false
0 0 '0' false
''(空字符串) 0 '' false
'123' 123 '123' true
'hello' NaN 'hello' true
NaN NaN 'NaN' false
[](空数组) 0 '' true
[1] 1 '1' true
{} NaN '[object Object]' true

疑惑:为什么 [] == false 是 true,但 if([]) 却进入了真分支?

答疑:这涉及两种不同的转换路径。if([]) 是将 [] 转为布尔值,非空对象一律为 true。而 [] == false 是抽象相等比较,两边都会经过 ToPrimitive 转换——[] 先调用 valueOf()(返回自身),再调用 toString()(返回 ""),最后 "" == false → 0 == 0 → true。

// 验证
console.log(Boolean([]));           // true(直接转布尔值)
console.log([] == false);           // true(抽象相等比较)
console.log([] == ![]);             // true(![] 为 false,回到上面的情况)
console.log({} == ![]);             // false

// Symbol.toPrimitive 自定义转换
const customObj = {
    [Symbol.toPrimitive](hint) {
        if (hint === 'number') return 42;
        if (hint === 'string') return 'custom';
        return true;  // default
    }
};
console.log(+customObj);           // 42
console.log(`${customObj}`);       // 'custom'
console.log(customObj + '');       // 'true'(default hint)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上次更新: 2026/06/10, 11:13:41
入门介绍
运算符

← 入门介绍 运算符→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式