Skip to content

JavaScript 进阶主题

本文将介绍一些 JavaScript 的进阶概念和特性,适合已经掌握基础知识的开发者学习。

闭包 (Closures)

闭包是 JavaScript 中非常重要的概念,它是指函数可以访问其定义时所在的词法作用域。

javascript
function createCounter() {
  let count = 0; // 私有变量

  return function() {
    count++; // 访问外部函数的变量
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

闭包常见用途:

  • 创建私有变量和方法
  • 实现数据封装
  • 保存函数执行状态

原型和原型链

JavaScript 使用原型链机制来实现继承。

javascript
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  return `Hello, my name is ${this.name}`;
};

const alice = new Person('Alice');
console.log(alice.sayHello()); // "Hello, my name is Alice"

原型链

javascript
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

// 设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

// 添加新方法
Student.prototype.getGrade = function() {
  return this.grade;
};

const bob = new Student('Bob', 'A');
console.log(bob.sayHello()); // "Hello, my name is Bob"
console.log(bob.getGrade()); // "A"

ES6+ 新特性

类 (Classes)

ES6 引入了类语法,让面向对象编程更加直观:

javascript
class Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    return `Hello, my name is ${this.name}`;
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }

  getGrade() {
    return this.grade;
  }
}

解构赋值

javascript
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a, b, rest); // 1, 2, [3, 4, 5]

// 对象解构
const { name, age, ...other } = { name: 'Alice', age: 25, city: 'New York', job: 'Developer' };
console.log(name, age, other); // "Alice", 25, { city: "New York", job: "Developer" }

模板字符串

javascript
const name = 'Alice';
const greeting = `Hello, ${name}!
Welcome to our website.`;

箭头函数

javascript
// 传统函数
function add(a, b) {
  return a + b;
}

// 箭头函数
const add = (a, b) => a + b;

// 箭头函数没有自己的 this
function Counter() {
  this.count = 0;

  setInterval(() => {
    // 这里的 this 指向 Counter 实例
    this.count++;
    console.log(this.count);
  }, 1000);
}

异步编程

Promise

Promise 是用于处理异步操作的对象:

javascript
function fetchData(url) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(data => resolve(data))
      .catch(error => reject(error));
  });
}

fetchData('https://api.example.com/data')
  .then(data => console.log('Data:', data))
  .catch(error => console.error('Error:', error));

Async/Await

async/await 是基于 Promise 的语法糖,使异步代码更易读:

javascript
async function fetchUserData(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    if (!response.ok) {
      throw new Error('Failed to fetch user data');
    }

    const userData = await response.json();
    return userData;
  } catch (error) {
    console.error('Error fetching user data:', error);
    throw error;
  }
}

// 使用
(async () => {
  try {
    const user = await fetchUserData(123);
    console.log('User:', user);
  } catch (error) {
    console.error('Failed to get user:', error);
  }
})();

函数式编程

JavaScript 支持函数式编程范式:

高阶函数

javascript
// 接收函数作为参数的函数
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, curr) => acc + curr, 0);

纯函数

纯函数没有副作用,对于相同的输入总是返回相同的输出:

javascript
// 纯函数
function add(a, b) {
  return a + b;
}

// 非纯函数(有副作用)
let total = 0;
function addToTotal(value) {
  total += value; // 修改外部状态
  return total;
}

函数组合

javascript
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);

const double = x => x * 2;
const square = x => x * x;
const addOne = x => x + 1;

const compute = compose(addOne, square, double);
console.log(compute(3)); // 等同于 addOne(square(double(3))) => 19

模块系统

ES Modules

javascript
// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export default function multiply(a, b) {
  return a * b;
}

// app.js
import multiply, { add, subtract } from './math.js';

console.log(add(5, 3));       // 8
console.log(subtract(10, 4)); // 6
console.log(multiply(2, 3));  // 6

元编程

Proxy

Proxy 允许你拦截和自定义对象的操作:

javascript
const handler = {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    } else {
      return `Property "${prop}" does not exist`;
    }
  },
  set(target, prop, value) {
    if (prop === 'age' && typeof value !== 'number') {
      throw new TypeError('Age must be a number');
    }
    target[prop] = value;
    return true;
  }
};

const person = new Proxy({}, handler);
person.name = 'Alice';
person.age = 25;

console.log(person.name);     // "Alice"
console.log(person.unknown);  // "Property "unknown" does not exist"

try {
  person.age = '26'; // Throws TypeError
} catch (e) {
  console.error(e.message);   // "Age must be a number"
}

Reflect

Reflect 是与 Proxy 搭配使用的 API,提供了操作对象的方法:

javascript
const obj = {
  name: 'Alice',
  age: 25
};

// 使用 Reflect 获取属性
console.log(Reflect.get(obj, 'name')); // "Alice"

// 检查属性是否存在
console.log(Reflect.has(obj, 'job')); // false

// 添加新属性
Reflect.set(obj, 'job', 'Developer');
console.log(obj.job); // "Developer"

内存管理和垃圾回收

JavaScript 有自动垃圾回收机制,但了解内存管理原则很重要:

内存泄漏

常见的内存泄漏场景:

javascript
// 1. 全局变量泄漏
function leakGlobal() {
  leakedVariable = 'I am leaked'; // 没有 var/let/const,成为全局变量
}

// 2. 闭包引用
function setupLeakyTimer() {
  const largeData = new Array(1000000).fill('x');

  setInterval(function() {
    // 引用了 largeData,即使不使用
    console.log('Timer fired, data length:', largeData.length);
  }, 1000);
}

// 3. 没有清理的事件监听器
function addButtonHandler() {
  const button = document.getElementById('button');
  const largeData = new Array(1000000).fill('x');

  button.addEventListener('click', function() {
    console.log('Button clicked, data length:', largeData.length);
  });

  // 移除时应该: button.removeEventListener(...)
}

避免内存泄漏的策略

javascript
// 1. 使用块级作用域
{
  const tempData = new Array(1000000).fill('x');
  // 在这个块中处理 tempData
} // tempData 在这里已经可以被回收

// 2. 及时清理定时器
const timerId = setInterval(() => {
  // 定期执行的代码
}, 1000);

// 当不再需要时清理
clearInterval(timerId);

// 3. 移除不再需要的事件监听器
function handleClick() {
  console.log('Button clicked');
}

button.addEventListener('click', handleClick);

// 当不再需要时移除
button.removeEventListener('click', handleClick);

// 4. 使用弱引用
const weakMap = new WeakMap();
{
  const obj = { data: 'some data' };
  weakMap.set(obj, 'metadata');
} // obj 可以被垃圾回收,weakMap 中的相应项也会被移除

Web API 和浏览器交互

Fetch API

javascript
async function fetchUsers() {
  try {
    const response = await fetch('https://api.example.com/users');

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const users = await response.json();
    return users;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

Web Storage

javascript
// localStorage - 持久存储
localStorage.setItem('user', JSON.stringify({ id: 1, name: 'Alice' }));
const user = JSON.parse(localStorage.getItem('user'));
localStorage.removeItem('user');

// sessionStorage - 会话存储
sessionStorage.setItem('token', 'abc123');
const token = sessionStorage.getItem('token');
sessionStorage.clear(); // 清除所有会话存储

性能优化技巧

防抖与节流

javascript
// 防抖 - 等待一段时间后执行,如果在等待期间再次调用则重新计时
function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

// 节流 - 固定时间间隔内最多执行一次
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => {
        inThrottle = false;
      }, limit);
    }
  };
}

// 使用示例
const debouncedSearch = debounce(searchFunction, 300);
const throttledScroll = throttle(scrollHandler, 100);

惰性加载

javascript
// 惰性加载函数
function createLazyFunction() {
  let result;

  return function() {
    if (result === undefined) {
      console.log('Computing expensive result...');
      // 假设这是一个昂贵的操作
      result = [1, 2, 3, 4, 5].map(x => x * x).reduce((a, b) => a + b, 0);
    }
    return result;
  };
}

const lazyGetResult = createLazyFunction();
console.log(lazyGetResult()); // 计算并返回结果
console.log(lazyGetResult()); // 直接返回缓存的结果

结语

本文介绍了 JavaScript 的多种进阶主题,帮助开发者从基础迈向更高级的编程水平。持续学习和实践这些概念,将使你能够编写更加健壮、高效和可维护的代码。

记住,精通这些概念需要时间和练习,建议结合实际项目逐步应用这些技术。

Released under the MIT License.