Skip to content

JavaScript 高级概念

本文探讨JavaScript的高级特性,包括闭包、原型链、异步编程、设计模式等进阶主题。

闭包

闭包是指函数和其周围的词法环境的组合,使函数可以访问其外部作用域中的变量。

javascript
function createCounter() {
  let count = 0; // 私有变量
  
  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getValue: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getValue());  // 1

闭包的应用场景

  1. 数据封装和私有变量
javascript
function createPerson(name) {
  // 私有变量
  let age = 0;
  
  return {
    getName: function() {
      return name;
    },
    getAge: function() {
      return age;
    },
    setAge: function(newAge) {
      if (newAge > 0 && newAge < 120) {
        age = newAge;
      }
    }
  };
}

const person = createPerson("Alice");
person.setAge(25);
console.log(person.getName()); // "Alice"
console.log(person.getAge());  // 25
  1. 函数工厂
javascript
function multiplyBy(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = multiplyBy(2);
const triple = multiplyBy(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15
  1. 模块模式
javascript
const calculator = (function() {
  // 私有变量和函数
  let result = 0;
  
  function validateNumber(num) {
    return typeof num === 'number' && !isNaN(num);
  }
  
  // 公共API
  return {
    add: function(num) {
      if (validateNumber(num)) {
        result += num;
      }
      return this;
    },
    subtract: function(num) {
      if (validateNumber(num)) {
        result -= num;
      }
      return this;
    },
    getResult: function() {
      return result;
    }
  };
})();

calculator.add(5).subtract(2).add(10);
console.log(calculator.getResult()); // 13

原型和原型链

JavaScript中的对象通过原型链实现继承。每个对象都有一个内部链接指向另一个对象,称为它的原型。

javascript
// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 在原型上添加方法
Person.prototype.greet = function() {
  return `Hello, my name is ${this.name}`;
};

// 创建实例
const alice = new Person("Alice", 25);
console.log(alice.greet()); // "Hello, my name is Alice"

// 原型链
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

原型继承

javascript
// 父构造函数
function Animal(name) {
  this.name = name;
}

Animal.prototype.makeSound = function() {
  return "Some generic sound";
};

// 子构造函数
function Dog(name, breed) {
  // 调用父构造函数
  Animal.call(this, name);
  this.breed = breed;
}

// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复constructor属性

// 添加/覆盖方法
Dog.prototype.makeSound = function() {
  return "Woof!";
};

Dog.prototype.getBreed = function() {
  return this.breed;
};

// 创建实例
const rex = new Dog("Rex", "German Shepherd");
console.log(rex.name);       // "Rex"
console.log(rex.makeSound()); // "Woof!"
console.log(rex.getBreed());  // "German Shepherd"

类与原型

ES6的类语法本质上是原型继承的语法糖。

javascript
// ES6类语法
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  makeSound() {
    return "Some generic sound";
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  makeSound() {
    return "Woof!";
  }
  
  getBreed() {
    return this.breed;
  }
}

// 创建实例
const max = new Dog("Max", "Beagle");
console.log(max.name);       // "Max"
console.log(max.makeSound()); // "Woof!"
console.log(max.getBreed());  // "Beagle"

高阶函数

高阶函数是指接受函数作为参数或返回函数的函数。

javascript
// 接受函数作为参数
function operate(a, b, operation) {
  return operation(a, b);
}

const add = (x, y) => x + y;
const multiply = (x, y) => x * y;

console.log(operate(5, 3, add));      // 8
console.log(operate(5, 3, multiply)); // 15

// 返回函数
function createValidator(regex) {
  return function(value) {
    return regex.test(value);
  };
}

const isEmail = createValidator(/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)*\.[a-z]{2,}$/i);
const isPhone = createValidator(/^\d{10}$/);

console.log(isEmail("test@example.com")); // true
console.log(isPhone("1234567890"));       // true

常用的高阶函数

javascript
const numbers = [1, 2, 3, 4, 5];

// map - 转换数组元素
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter - 过滤数组元素
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]

// reduce - 将数组归约为单个值
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15

// forEach - 遍历数组元素
numbers.forEach(num => console.log(num));

// some - 检查是否至少有一个元素满足条件
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true

// every - 检查是否所有元素都满足条件
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true

异步编程

回调函数

javascript
function fetchData(callback) {
  setTimeout(() => {
    const data = { id: 1, name: "Product" };
    callback(null, data);
  }, 1000);
}

fetchData((error, data) => {
  if (error) {
    console.error("Error:", error);
  } else {
    console.log("Data:", data);
  }
});

Promise

javascript
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true;
      if (success) {
        resolve({ id: 1, name: "Product" });
      } else {
        reject(new Error("Failed to fetch data"));
      }
    }, 1000);
  });
}

fetchData()
  .then(data => {
    console.log("Data:", data);
    return processData(data);
  })
  .then(result => {
    console.log("Result:", result);
  })
  .catch(error => {
    console.error("Error:", error);
  });

function processData(data) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ ...data, processed: true });
    }, 500);
  });
}

Async/Await

javascript
async function fetchAndProcessData() {
  try {
    const data = await fetchData();
    console.log("Data:", data);
    
    const result = await processData(data);
    console.log("Result:", result);
    
    return result;
  } catch (error) {
    console.error("Error:", error);
    throw error;
  }
}

fetchAndProcessData()
  .then(finalResult => {
    console.log("Final result:", finalResult);
  })
  .catch(error => {
    console.error("Caught error:", error);
  });

设计模式

单例模式

javascript
const Singleton = (function() {
  let instance;
  
  function createInstance() {
    return {
      name: "Singleton Instance",
      method() {
        return "Method called";
      }
    };
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

工厂模式

javascript
function createUser(type) {
  const types = {
    admin: AdminUser,
    regular: RegularUser,
    guest: GuestUser
  };
  
  const UserType = types[type] || RegularUser;
  return new UserType();
}

function AdminUser() {
  this.role = "admin";
  this.permissions = ["read", "write", "delete"];
}

function RegularUser() {
  this.role = "regular";
  this.permissions = ["read", "write"];
}

function GuestUser() {
  this.role = "guest";
  this.permissions = ["read"];
}

const admin = createUser("admin");
const regular = createUser("regular");
const guest = createUser("guest");

console.log(admin.permissions);  // ["read", "write", "delete"]
console.log(regular.permissions); // ["read", "write"]
console.log(guest.permissions);   // ["read"]

观察者模式

javascript
class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
    return this;
  }
  
  off(event, listener) {
    if (!this.events[event]) return this;
    
    this.events[event] = this.events[event].filter(l => l !== listener);
    return this;
  }
  
  emit(event, ...args) {
    if (!this.events[event]) return false;
    
    this.events[event].forEach(listener => {
      listener.apply(this, args);
    });
    return true;
  }
  
  once(event, listener) {
    const onceWrapper = (...args) => {
      listener.apply(this, args);
      this.off(event, onceWrapper);
    };
    
    this.on(event, onceWrapper);
    return this;
  }
}

const emitter = new EventEmitter();

function onMessage(message) {
  console.log("Message received:", message);
}

emitter.on("message", onMessage);
emitter.emit("message", "Hello World"); // "Message received: Hello World"

emitter.once("special", message => {
  console.log("Special message:", message);
});

emitter.emit("special", "One time");     // "Special message: One time"
emitter.emit("special", "Won't show");   // 不会触发

模块模式

javascript
const ShoppingCart = (function() {
  // 私有变量
  let items = [];
  let total = 0;
  
  // 私有方法
  function calculateTotal() {
    return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  }
  
  // 公共API
  return {
    addItem: function(item) {
      items.push(item);
      total = calculateTotal();
    },
    removeItem: function(id) {
      items = items.filter(item => item.id !== id);
      total = calculateTotal();
    },
    getItems: function() {
      return [...items]; // 返回副本,防止外部修改
    },
    getTotal: function() {
      return total;
    },
    clearCart: function() {
      items = [];
      total = 0;
    }
  };
})();

ShoppingCart.addItem({ id: 1, name: "Product 1", price: 10, quantity: 2 });
ShoppingCart.addItem({ id: 2, name: "Product 2", price: 15, quantity: 1 });

console.log(ShoppingCart.getItems()); // [{id: 1, ...}, {id: 2, ...}]
console.log(ShoppingCart.getTotal()); // 35

ShoppingCart.removeItem(1);
console.log(ShoppingCart.getTotal()); // 15

函数式编程

纯函数

javascript
// 纯函数 - 相同输入总是产生相同输出,没有副作用
function add(a, b) {
  return a + b;
}

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

不可变性

javascript
// 不修改原始数据
const originalArray = [1, 2, 3];

// 不好的做法 - 修改原始数组
originalArray.push(4); // 有副作用

// 好的做法 - 创建新数组
const newArray = [...originalArray, 4]; // 无副作用

// 对象不可变更新
const user = { name: "Alice", age: 25 };
const updatedUser = { ...user, age: 26 }; // 创建新对象而不是修改原对象

函数组合

javascript
// 简单函数
const double = x => x * 2;
const increment = x => x + 1;
const square = x => x * x;

// 函数组合
function compose(...fns) {
  return function(x) {
    return fns.reduceRight((value, fn) => fn(value), x);
  };
}

const transformValue = compose(square, increment, double);
// 等价于 square(increment(double(x)))

console.log(transformValue(3)); // 49 (square(increment(double(3))) = square(increment(6)) = square(7) = 49)

柯里化

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

// 柯里化函数
function curryAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

// 使用箭头函数简化
const curryAddArrow = a => b => c => a + b + c;

console.log(add(1, 2, 3));           // 6
console.log(curryAdd(1)(2)(3));      // 6
console.log(curryAddArrow(1)(2)(3)); // 6

// 通用柯里化函数
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...moreArgs) {
        return curried.apply(this, args.concat(moreArgs));
      };
    }
  };
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3));  // 6
console.log(curriedAdd(1, 2)(3));   // 6
console.log(curriedAdd(1)(2, 3));   // 6
console.log(curriedAdd(1, 2, 3));   // 6

内存管理与垃圾回收

javascript
// 内存泄漏示例 - 闭包引用
function createLeak() {
  const largeData = new Array(1000000).fill('x'); // 大量数据
  
  return function() {
    // 这个函数引用了largeData,即使函数本身不使用它
    console.log("Function called");
    // 如果不小心引用了largeData,它将保留在内存中
    // console.log(largeData[0]);
  };
}

const leakyFunction = createLeak(); // largeData在内存中保留

// 避免内存泄漏
function createNonLeak() {
  const largeData = new Array(1000000).fill('x');
  const result = processData(largeData);
  
  return function() {
    console.log("Result:", result);
    // 不再引用largeData,它可以被垃圾回收
  };
}

function processData(data) {
  return data.length;
}

const nonLeakyFunction = createNonLeak(); // largeData可以被垃圾回收

元编程

Proxy

javascript
const user = {
  firstName: "John",
  lastName: "Doe",
  age: 30
};

const userProxy = new Proxy(user, {
  get(target, property) {
    console.log(`Getting property: ${property}`);
    
    if (property === 'fullName') {
      return `${target.firstName} ${target.lastName}`;
    }
    
    return target[property];
  },
  
  set(target, property, value) {
    console.log(`Setting property: ${property} to ${value}`);
    
    if (property === 'age' && typeof value !== 'number') {
      throw new TypeError('Age must be a number');
    }
    
    if (property === 'age' && (value < 0 || value > 120)) {
      throw new RangeError('Age must be between 0 and 120');
    }
    
    target[property] = value;
    return true; // 表示设置成功
  }
});

console.log(userProxy.firstName);  // "Getting property: firstName" "John"
console.log(userProxy.fullName);   // "Getting property: fullName" "John Doe"

userProxy.age = 31;                // "Setting property: age to 31"
// userProxy.age = -5;             // RangeError: Age must be between 0 and 120
// userProxy.age = "thirty";       // TypeError: Age must be a number

Reflect

javascript
const user = {
  name: "Alice",
  age: 25,
  greet() {
    return `Hello, my name is ${this.name}`;
  }
};

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

// 设置属性
Reflect.set(user, 'age', 26);
console.log(user.age);  // 26

// 检查属性是否存在
console.log(Reflect.has(user, 'name'));  // true
console.log(Reflect.has(user, 'salary')); // false

// 删除属性
Reflect.deleteProperty(user, 'age');
console.log(user.age);  // undefined

// 调用方法
console.log(Reflect.apply(user.greet, user, []));  // "Hello, my name is Alice"

// 创建新对象
const newUser = Reflect.construct(function(name) {
  this.name = name;
}, ["Bob"]);
console.log(newUser.name);  // "Bob"

Symbol

javascript
// 使用Symbol定义特殊方法
const SIZE = Symbol('size');

class Collection {
  constructor() {
    this[SIZE] = 0;
    this.items = {};
  }
  
  add(item) {
    this.items[this[SIZE]] = item;
    this[SIZE]++;
  }
  
  get size() {
    return this[SIZE];
  }
}

const collection = new Collection();
collection.add("item 1");
collection.add("item 2");

console.log(collection.size);  // 2
console.log(Object.keys(collection));  // ["items"] (SIZE不可见)

// 内置Symbol
const iterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (const value of iterable) {
  console.log(value);  // 1, 2, 3
}

Web API 和浏览器环境

DOM 操作

javascript
// 选择元素
const element = document.getElementById('myElement');
const elements = document.querySelectorAll('.myClass');

// 修改内容和属性
element.textContent = 'New text';
element.innerHTML = '<strong>Bold text</strong>';
element.setAttribute('data-custom', 'value');

// 样式操作
element.style.color = 'red';
element.style.backgroundColor = '#f0f0f0';
element.classList.add('highlight');
element.classList.remove('hidden');
element.classList.toggle('active');

// 创建和添加元素
const newElement = document.createElement('div');
newElement.textContent = 'New element';
document.body.appendChild(newElement);

// 事件处理
element.addEventListener('click', function(event) {
  console.log('Element clicked', event);
});

// 事件委托
document.getElementById('parent').addEventListener('click', function(event) {
  if (event.target.matches('.child')) {
    console.log('Child element clicked', event.target);
  }
});

浏览器存储

javascript
// localStorage - 持久存储
localStorage.setItem('username', 'john_doe');
const username = localStorage.getItem('username');
console.log(username);  // "john_doe"
localStorage.removeItem('username');
localStorage.clear();  // 清除所有数据

// sessionStorage - 会话存储
sessionStorage.setItem('tempData', JSON.stringify({ id: 123 }));
const data = JSON.parse(sessionStorage.getItem('tempData'));
console.log(data.id);  // 123

// Cookies
document.cookie = "name=John; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/";

// 读取cookies
function getCookie(name) {
  const cookies = document.cookie.split('; ');
  for (const cookie of cookies) {
    const [cookieName, cookieValue] = cookie.split('=');
    if (cookieName === name) {
      return decodeURIComponent(cookieValue);
    }
  }
  return null;
}

console.log(getCookie('name'));  // "John"

网络请求

javascript
// Fetch API
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Data received:', data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

// POST请求
fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'John',
    email: 'john@example.com'
  })
})
.then(response => response.json())
.then(data => console.log('Response:', data));

// async/await 与 Fetch
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

性能优化

防抖和节流

javascript
// 防抖 - 延迟执行,重置计时器
function debounce(func, delay) {
  let timeoutId;
  
  return function(...args) {
    const context = this;
    
    clearTimeout(timeoutId);
    
    timeoutId = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}

// 使用防抖
const debouncedSearch = debounce(function(query) {
  console.log('Searching for:', query);
  // 执行搜索操作
}, 300);

// 在输入框中使用
// inputElement.addEventListener('input', e => debouncedSearch(e.target.value));

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

// 使用节流
const throttledScroll = throttle(function() {
  console.log('Scroll event throttled');
  // 执行滚动处理
}, 500);

// 在滚动事件中使用
// window.addEventListener('scroll', throttledScroll);

懒加载

javascript
// 图片懒加载
function lazyLoadImages() {
  const images = document.querySelectorAll('img[data-src]');
  
  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.removeAttribute('data-src');
        observer.unobserve(img);
      }
    });
  });
  
  images.forEach(img => imageObserver.observe(img));
}

// 组件懒加载
function lazyLoadComponent(componentId, url) {
  const placeholder = document.getElementById(componentId);
  
  const componentObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        fetch(url)
          .then(response => response.text())
          .then(html => {
            placeholder.innerHTML = html;
            observer.unobserve(placeholder);
          });
      }
    });
  });
  
  componentObserver.observe(placeholder);
}

Web Workers

javascript
// 主线程代码
function startWorker() {
  const worker = new Worker('worker.js');
  
  worker.addEventListener('message', event => {
    console.log('Result from worker:', event.data);
  });
  
  worker.addEventListener('error', error => {
    console.error('Worker error:', error);
  });
  
  // 发送数据给Worker
  worker.postMessage({
    numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    operation: 'sum'
  });
}

// worker.js 内容
/*
self.addEventListener('message', event => {
  const { numbers, operation } = event.data;
  
  let result;
  switch (operation) {
    case 'sum':
      result = numbers.reduce((sum, num) => sum + num, 0);
      break;
    case 'average':
      result = numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
      break;
    default:
      throw new Error(`Unknown operation: ${operation}`);
  }
  
  // 发送结果回主线程
  self.postMessage(result);
});
*/