01 - JS 基础
目录
一、数据类型
基本数据类型(原始类型)
| 类型 | 说明 |
|---|---|
number | IEEE 754 双精度浮点数 |
string | 字符串 |
boolean | 布尔值 |
null | 空值(历史原因 typeof null === 'object') |
undefined | 未定义 |
symbol | 唯一性标识符(ES6) |
bigint | 任意精度整数(ES2020) |
null不是对象。typeof null === 'object'是历史遗留 bug(null 的二进制全为 0,被当成了 object 前缀000)。
引用数据类型
对象(Object)、数组、函数等。
引用类型的变量保存的是内存地址(指针),而非值本身。
null也是引用类型,指针指向空地址。
Symbol 的主要用途
唯一性标识符:每个
Symbol()创建的值都是唯一的,避免属性名冲突。jsconst sym1 = Symbol('id'); const sym2 = Symbol('id'); console.log(sym1 === sym2); // false隐藏属性:Symbol 作为属性键,不会出现在
for...in或Object.keys()中。内部协议与元编程:内置 Symbol 如
Symbol.iterator用于定义对象的迭代行为。jsconst myIterable = { [Symbol.iterator]() { let count = 0; return { next() { if (count < 3) return { value: count++, done: false }; return { done: true }; } }; } }; for (let value of myIterable) { console.log(value); // 输出 0, 1, 2 }
二、类型判断
typeof
js
typeof undefined; // "undefined"
typeof "Hello"; // "string"
typeof 123; // "number"
typeof true; // "boolean"
typeof Symbol(); // "symbol"
typeof 10n; // "bigint"
typeof function(){}; // "function"
typeof {}; // "object"
typeof []; // "object" ⚠️ 数组也是 object
typeof null; // "object" ⚠️ 历史遗留问题typeof 能力:
- 识别所有值类型
- 识别函数
- 判断是否是引用类型(但无法区分具体引用类型)
Object.prototype.toString.call()(更精确)
js
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(/abc/); // "[object RegExp]"instanceof
js
[] instanceof Array; // true
[] instanceof Object; // true⚠️ 跨 iframe / 跨窗口创建的对象,
instanceof可能不准确。
三、类型转换
默认转换规则
number与string遇到-*/%==时,字符串转数字- 遇到
+时,数字转字符串再拼接(任何类型与字符串相加都是字符串)
显式转换
| 目标 | 方法 |
|---|---|
| 字符串转数字 | parseInt(), parseFloat(), Number() |
| 数字转字符串 | String(), JSON.stringify(), +'' |
| 转布尔值 | Boolean()(null、''、0、undefined、NaN → false,空对象 → true) |
| 布尔转数字 | Number()(true → 1,false → 0) |
字符串拼接陷阱
js
const a = 100 + 10; // 110(数字相加)
const b = 100 + '10'; // "10010"(字符串拼接)
const c = true + '10'; // "true10"解决方法:用 parseInt() / parseFloat() 显式转换。
== 运算符的隐式转换

x == y
│
├── 类型相同 → 比较值(如 x === y)
├── null vs undefined → true
├── 数字 vs 字符串 → 转数字后比较
├── 布尔值 → 转数字后比较
├── 对象 vs 原始类型 → 对象转原始值后比较
└── 其他 → false(其他类型与 null/undefined 比较均为 false)规则:除了
== null之外,其他一律用===。
对象转原始类型([[ToPrimitive]])
调用优先级:Symbol.toPrimitive > valueOf() > toString()
js
let a = {
valueOf() { return 0 },
toString() { return '1' },
[Symbol.toPrimitive]() { return 2 }
}
1 + a // => 3(Symbol.toPrimitive 优先级最高)JS 类型转换总览

if 语句中的真假值
js
// falsely 变量(!!a === false):
// 0、''、NaN、null、undefined、false
// 其余均为 truly 变量
四、值类型 vs 引用类型

- 值类型:存储在栈中,赋值是直接复制值(
number、string、boolean、null、undefined、symbol) - 引用类型:存储在堆中,赋值复制的是内存地址(对象、数组、函数)
function是特殊的引用类型,用于存储逻辑,没有"拷贝函数"的说法。
五、深拷贝
JSON 方式(简单但有缺陷)
js
const copy = JSON.parse(JSON.stringify(obj));缺陷:
- 无法处理函数、Symbol、undefined(会被忽略或转为
null) - 不能处理循环引用(会报错)
- Date 转为字符串,RegExp 转为空对象
- 丢失原型链和方法
手写深拷贝
js
const deepClone = (obj = {}) => {
// 值类型 或 null,直接返回
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 区分数组与对象
let result = obj instanceof Array ? [] : {};
for (let key in obj) {
// 只拷贝自身属性,不拷贝原型链上的属性
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
};六、原型与原型链
JavaScript 基于原型继承。
- 每个 class(构造函数)都有显式原型
prototype - 每个实例都有隐式原型
__proto__ - 获取属性/方法时:先在自身查找 → 再去
__proto__查找 → 一直到Object.prototype
js
stu.__proto__ === Student.prototype // true

instanceof 原理
沿着对象的原型链向上查找,判断是否能找到目标类的 prototype:
js
student instanceof Student // true
student instanceof People // true(父类)
student instanceof Object // true(所有对象的顶级原型)判断自身属性
js
stu.hasOwnProperty('func')
// 判断 func 是否是实例自身的属性(而非继承自父类)JS 对象原型链图

七、Class 继承
class 是 ES6 语法糖,本质还是基于原型的对象模型。
js
class Student extends People {
constructor(父类参数, 子类参数) {
super(父类参数); // 调用父类构造函数
this.子类参数 = 子类参数;
}
}等价于(ES5 原型实现):
js
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};方法共享:方法定义在 prototype 上,所有实例共享,节约内存。
如果使用
class定义了自定义类,对其使用typeof会返回"function"。
八、super 关键字
1. 在构造函数中调用父类构造函数
js
class Parent {
constructor(name) {
this.name = name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 必须调用,才能在子类中使用 this
this.age = age;
}
}2. 在派生类中访问父类方法
js
class Parent {
greet() {
return 'Hello from Parent';
}
}
class Child extends Parent {
greet() {
return super.greet() + ' - Hello from Child';
}
}
const child = new Child();
console.log(child.greet()); // "Hello from Parent - Hello from Child"