JS进阶

JS进阶

_

一、作用域 (Scope)

作用域决定了变量和函数的可访问性(生命周期)。理解作用域是理解闭包和this的基础。

1.1 全局作用域

  • 在函数和代码块 {} 之外定义的变量。
  • 在浏览器中,全局作用域是 window,在Node.js中是 global
  • 慎用:过多的全局变量容易造成命名冲突和污染。

1.2 函数作用域

  • 在函数内部定义的变量,只能在函数内部访问。
  • 适用于 var 声明的变量。
function func() {
  var a = 10; // 函数作用域
  console.log(a); // 10
}
console.log(a); // ReferenceError: a is not defined

1.3 块级作用域

  • ES6 引入 letconst,实现了块级作用域。
  • {} 包裹的代码块(如 ifforwhile)。
if (true) {
  let b = 20;
  const c = 30;
  var d = 40; // 没有块级作用域,会泄露到全局
}
console.log(d); // 40
console.log(b); // ReferenceError: b is not defined

1.4 词法作用域(静态作用域)

  • 函数的作用域在定义时确定,而非调用时。
  • 作用域链:当在函数内部访问变量时,会从当前作用域向上级作用域逐级查找,直到全局作用域。
let name = "Global";
function outer() {
  let name = "Outer";
  function inner() {
    console.log(name);
  }
  inner(); // 输出 "Outer"
}
outer();

二、解构 (Destructuring)

解构是一种方便快捷的从数组或对象中提取值并赋给变量的语法糖。

2.1 数组解构

  • 按位置匹配。
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a, b, c); // 1 2 3

// 跳过元素、设置默认值
const [x, , y, z = 10] = [4, 5, 6];
console.log(x, y, z); // 4 6 10

// 交换变量
let i = 1, j = 2;
[i, j] = [j, i];
console.log(i, j); // 2 1

2.2 对象解构

  • 按属性名匹配,顺序无关。
const person = { name: "Alice", age: 25, city: "Beijing" };
const { name, age } = person;
console.log(name, age); // Alice 25

// 别名与默认值
const { name: userName, gender = "Unknown" } = person;
console.log(userName, gender); // Alice Unknown

// 嵌套解构
const student = { info: { id: 101, score: 95 } };
const { info: { id, score } } = student;
console.log(id, score); // 101 95

2.3 函数参数解构

  • 极大提高可读性,常用于配置对象。
function greet({ name, age = 18 }) {
  console.log(`Hello, I'm ${name}, ${age} years old.`);
}
greet({ name: "Bob" }); // Hello, I'm Bob, 18 years old.

三、箭头函数 (Arrow Function)

箭头函数提供了一种更简洁的函数书写方式,并在 this 处理上有根本性不同。

3.1 基本语法

  • (参数) => { 函数体 }
  • 只有一个参数时可省略小括号;函数体只有一条返回语句时可省略 {}return
const add = (a, b) => a + b;
const square = x => x * x;
const sayHi = () => console.log("Hi!");

3.2 与普通函数的区别(重点)

  • 没有自己的 thisthis 指向定义时所在的外部作用域(继承外层),且无法通过 callapplybind 改变。
  • 不能作为构造函数:不能使用 new 调用。
  • 没有 arguments 对象:可以使用 rest 参数 ...args 代替。
  • 没有 prototype 属性
const obj = {
  name: "FrontEnd",
  regular: function() {
    setTimeout(function() {
      console.log(this.name); // undefined (指向window/global)
    }, 100);
  },
  arrow: function() {
    setTimeout(() => {
      console.log(this.name); // FrontEnd (指向外层obj)
    }, 100);
  }
};
obj.regular();
obj.arrow();

四、构造函数 (Constructor)

构造函数是用于创建和初始化对象的特殊函数。ES6 之前,这是实现“类”的主要方式。

4.1 基本使用

  • 函数名首字母大写(约定)。
  • 使用 new 关键字调用。
  • 构造函数内部使用 this 绑定新对象的属性和方法。
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHi = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}
const p1 = new Person("Tom", 20);
p1.sayHi(); // Hi, I'm Tom

4.2 原型链与共享方法

  • 构造函数的 prototype 属性指向原型对象,原型对象上的方法被所有实例共享。
  • 每个实例通过 __proto__ 指向构造函数的 prototype
Person.prototype.eat = function() {
  console.log(`${this.name} is eating.`);
};
p1.eat(); // Tom is eating.
console.log(p1.__proto__ === Person.prototype); // true

4.3 实现继承

  • 原型链继承Child.prototype = new Parent()
  • 组合继承(借用构造函数 + 原型链)
  • 寄生组合继承(最理想方式)
function Student(name, age, grade) {
  Person.call(this, name, age); // 继承属性
  this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

const s1 = new Student("Jerry", 22, "A");
s1.sayHi(); // Hi, I'm Jerry

五、数据常用函数

掌握数组和对象的常用高级函数,能让你写出更简洁高效的代码。

5.1 数组函数(高阶函数为主)

  • forEach:遍历,不返回新数组。
  • map:映射,返回新数组。
  • filter:过滤,返回满足条件的新数组。
  • reduce:累加器,强大且灵活。
  • some / every:判断是否至少一个/全部满足条件。
  • find / findIndex:查找第一个满足条件的元素或索引。
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(n => n * 2); // [2,4,6,8,10]
const evens = nums.filter(n => n % 2 === 0); // [2,4]
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 15
const hasEven = nums.some(n => n % 2 === 0); // true

5.2 对象函数

  • Object.keys():获取所有键名(数组)。
  • Object.values():获取所有值。
  • Object.entries():获取键值对数组。
  • Object.assign():合并对象(浅拷贝)。
  • Object.freeze():冻结对象,不可修改。
const obj = { a: 1, b: 2 };
console.log(Object.entries(obj)); // [['a',1], ['b',2]]
const merged = Object.assign({}, obj, { c: 3 });

六、面向对象 (OOP)

ES6 引入 class 语法糖,让 JS 的面向对象更清晰、更接近传统语言。

6.1 Class 基本语法

  • constructor 是构造方法。
  • 方法直接定义在 class 中,自动挂在原型上。
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

6.2 静态属性与方法

  • 使用 static 关键字,属于类本身,不属于实例。
class MathUtil {
  static PI = 3.14159;
  static areaOfCircle(radius) {
    return this.PI * radius * radius;
  }
}
console.log(MathUtil.areaOfCircle(5));

6.3 继承 (extends)

  • 使用 extends 继承父类。
  • 子类 constructor 中必须调用 super() 才能使用 this
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造
    this.breed = breed;
  }
  speak() {
    console.log(`${this.name} barks.`);
  }
}
const d = new Dog("Buddy", "Golden");
d.speak(); // Buddy barks.

6.4 私有属性 (Private Fields)

  • 使用 # 前缀,ES2022 正式支持。
class Wallet {
  #balance = 0;
  deposit(amount) {
    this.#balance += amount;
  }
  getBalance() {
    return this.#balance;
  }
}

七、高阶技巧

这些技巧能帮你写出更优雅、健壮和高效的代码。

7.1 闭包 (Closure)

  • 定义:函数 + 其词法环境的组合。即内部函数可以访问外部函数的变量,即使外部函数已执行完毕。
  • 用途:数据私有化、柯里化、防抖/节流。
function counter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  };
}
const myCounter = counter();
myCounter(); // 1
myCounter(); // 2

7.2 防抖 (Debounce) 与节流 (Throttle)

  • 防抖:在连续触发中,只执行最后一次。场景:搜索输入框。
  • 节流:在连续触发中,每隔一段时间执行一次。场景:滚动监听。
// 防抖简易实现
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

7.3 深拷贝与浅拷贝

  • 浅拷贝Object.assign()、扩展运算符 ...(仅拷贝一层)。
  • 深拷贝JSON.parse(JSON.stringify(obj))(有局限,无法处理函数、循环引用等),或使用 structuredClone()(现代浏览器/Node.js 17+)。

7.4 Promise 与异步进阶

  • Promise.all:所有Promise成功才成功,返回所有结果数组。
  • Promise.race:返回第一个完成的Promise结果。
  • Promise.allSettled:所有Promise完成(无论成功/失败)后返回。
  • async/await:同步写法处理异步,更优雅。

7.5 可选链 (?.) 与空值合并 (??)

  • 可选链:安全地访问深层嵌套属性,避免 Cannot read property of undefined
  • 空值合并:只有当左侧为 nullundefined 时,才返回右侧值。
const user = { profile: { name: "John" } };
console.log(user?.profile?.age ?? "Age unknown"); // Age unknown
正则表达式 2026-06-09

评论区

© 2026 日志记录