Skip to content

01 - JS 基础

目录


一、数据类型

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

类型说明
numberIEEE 754 双精度浮点数
string字符串
boolean布尔值
null空值(历史原因 typeof null === 'object'
undefined未定义
symbol唯一性标识符(ES6)
bigint任意精度整数(ES2020)

null 不是对象。typeof null === 'object' 是历史遗留 bug(null 的二进制全为 0,被当成了 object 前缀 000)。

引用数据类型

对象(Object)、数组、函数等。

引用类型的变量保存的是内存地址(指针),而非值本身。null 也是引用类型,指针指向空地址。

Symbol 的主要用途

  1. 唯一性标识符:每个 Symbol() 创建的值都是唯一的,避免属性名冲突。

    js
    const sym1 = Symbol('id');
    const sym2 = Symbol('id');
    console.log(sym1 === sym2); // false
  2. 隐藏属性:Symbol 作为属性键,不会出现在 for...inObject.keys() 中。

  3. 内部协议与元编程:内置 Symbol 如 Symbol.iterator 用于定义对象的迭代行为。

    js
    const 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 可能不准确。


三、类型转换

默认转换规则

  • numberstring 遇到 - * / % == 时,字符串转数字
  • 遇到 + 时,数字转字符串再拼接(任何类型与字符串相加都是字符串

显式转换

目标方法
字符串转数字parseInt(), parseFloat(), Number()
数字转字符串String(), JSON.stringify(), +''
转布尔值Boolean()null''0undefinedNaNfalse,空对象 → true
布尔转数字Number()true1false0

字符串拼接陷阱

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 类型转换总览

JS类型转换

if 语句中的真假值

js
// falsely 变量(!!a === false):
// 0、''、NaN、null、undefined、false
// 其余均为 truly 变量

if语句逻辑判断


四、值类型 vs 引用类型

值类型与引用类型内存示意

  • 值类型:存储在栈中,赋值是直接复制值(numberstringbooleannullundefinedsymbol
  • 引用类型:存储在堆中,赋值复制的是内存地址(对象、数组、函数)

function 是特殊的引用类型,用于存储逻辑,没有"拷贝函数"的说法。


五、深拷贝

JSON 方式(简单但有缺陷)

js
const copy = JSON.parse(JSON.stringify(obj));

缺陷

  • 无法处理函数Symbolundefined(会被忽略或转为 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 对象原型链图

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"

<< 返回首页