JavaScript设计模式 / 高阶函数 / 柯里化函数 - 汇总

15 阅读5分钟

§ 创建型模式

1. 工厂模式 (Factory Pattern)

// 工厂函数创建不同类型的产品
class ProductA { 
    getName() { 
        return 'Product A'; 
    }
}
class ProductB {
    getName() {
        return 'Product B';
    }
}
function createProduct(type) {
    if (type === 'A') {
        return new ProductA();
    } else if (type === 'B') {
        return new ProductB();
    }
}
const product1 = createProduct('A');
console.log(product1.getName()); // 输出: Product A

特性:通过工厂函数创建对象,隐藏具体实现细节,客户端只需知道接口。

应用场景:动态创建不同类型对象,如表单控件生成、日志记录器。

  • 优点:解耦客户端与具体产品类,符合单一职责原则

  • 缺点:添加新产品需要修改工厂类,违反开闭原则

1. Q: 工厂模式与直接使用new创建对象有什么区别?

A: 工厂模式将对象创建逻辑集中管理,客户端无需知道具体类名,符合"开闭原则"(对扩展开放,对修改关闭),当需要添加新产品时只需扩展工厂类而不用修改客户端代码。

2. Q: 什么时候应该使用简单工厂而不是抽象工厂?

A: 当产品种类较少且不会频繁变化时使用简单工厂;当产品族需要一起创建或有复杂层次结构时使用抽象工厂。

2. 单例模式 (Singleton Pattern)

const Singleton = (function() {
  let instance;
  function createInstance() {
    return {
      name: 'Singleton Instance',
      getName() {
        return this.name;
      }
    };
  }
  return {
    getInstance() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // 输出: true

特性:确保类只有一个实例,并提供全局访问点。

应用场景:全局状态管理、配置对象、数据库连接池。

  • 优点:严格控制实例访问,节省内存

  • 缺点:难以测试(全局状态),违反单一职责原则

3. 建造者模式(Builder Pattern)

class CarBuilder {
  constructor() {
    this.car = {};
  }
 
  setModel(model) {
    this.car.model = model;
    return this;
  }
 
  setColor(color) {
    this.car.color = color;
    return this;
  }
 
  build() {
    return this.car;
  }
}
 
const car = new CarBuilder()
  .setModel('Tesla')
  .setColor('Red')
  .build();
 
console.log(car); // 输出: { model: 'Tesla', color: 'Red' }

特性:分步骤构建复杂对象,允许客户端灵活配置。

应用场景:分步创建复杂对象,如文件上传配置、UI 组件构建。

4. 原型模式(Prototype Pattern)

const userPrototype = {
  name: 'Default User',
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  },
  clone() {
    return Object.create(this);
  }
};
 
const user1 = userPrototype.clone();
user1.name = 'Alice';
user1.sayHello(); // 输出: Hello, I'm Alice
 
const user2 = userPrototype.clone();
user2.name = 'Bob';
user2.sayHello(); // 输出: Hello, I'm Bob

特性:通过克隆现有对象创建新实例,避免重复初始化。

应用场景:对象初始化成本高时,如复杂 DOM 节点、游戏角色。

§ 结构型模式

1. 适配器模式(Adapter Pattern)

// 旧接口
class OldMap {
  display() {
    console.log('Displaying old map');
  }
}
 
// 新接口
class NewMap {
  show() {
    console.log('Displaying new map');
  }
}
 
// 适配器
class MapAdapter {
  constructor(map) {
    this.map = map;
  }
 
  display() {
    if (this.map.show) {
      this.map.show();
    }
  }
}
 
const oldMap = new OldMap();
const newMap = new NewMap();
const adaptedMap = new MapAdapter(newMap);
 
oldMap.display(); // 输出: Displaying old map
adaptedMap.display(); // 输出: Displaying new map

特性:使不兼容接口能够一起工作

应用场景:集成第三方库、新旧接口兼容

2. 装饰器模式(Decorator Pattern)

class Coffee {
  cost() {
    return 5;
  }
}
 
class CoffeeDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
 
  cost() {
    return this.coffee.cost();
  }
}
 
class MilkDecorator extends CoffeeDecorator {
  cost() {
    return super.cost() + 2;
  }
}
 
class SugarDecorator extends CoffeeDecorator {
  cost() {
    return super.cost() + 1;
  }
}
 
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.cost()); // 输出: 8

特性:动态为对象添加功能,不修改原对象。

应用场景:扩展对象功能,如日志记录、权限校验。

  • 优点:比继承更灵活,符合开闭原则

  • 缺点:多层装饰会增加复杂性,调试困难

3. 代理模式(Proxy Pattern)

// 真实对象
class RealSubject {
  request() {
    console.log('RealSubject: Handling request.');
    return 'real-data';
  }
}
 
// 代理对象
class ProxySubject {
  constructor(realSubject) {
    this.realSubject = realSubject;
  }
 
  request() {
    if (this.checkAccess()) {
      console.log('Proxy: Pre-processing request.');
      const result = this.realSubject.request();
      console.log('Proxy: Post-processing request.');
      return result;
    }
  }
 
  checkAccess() {
    console.log('Proxy: Checking access permissions.');
    return true;
  }
}
 
const realSubject = new RealSubject();
const proxy = new ProxySubject(realSubject);
console.log(proxy.request());
/* 输出:
Proxy: Checking access permissions.
Proxy: Pre-processing request.
RealSubject: Handling request.
Proxy: Post-processing request.
real-data
*/

特性:为对象提供代理,控制访问或延迟初始化。

应用场景:延迟加载、访问控制。

1. Q: 装饰器模式与代理模式有什么区别?

A: 关键区别在于目的:

装饰器:动态添加功能(透明增强)

代理:控制访问(可能限制功能)

装饰器通常透明地增强对象,而代理可能完全改变行为

4. 外观模式(Facade Pattern)

class SubsystemA {
  operationA() {
    console.log('Subsystem A operation');
  }
}
 
class SubsystemB {
  operationB() {
    console.log('Subsystem B operation');
  }
}
 
class Facade {
  constructor() {
    this.subsystemA = new SubsystemA();
    this.subsystemB = new SubsystemB();
  }
 
  operation() {
    this.subsystemA.operationA();
    this.subsystemB.operationB();
  }
}
 
const facade = new Facade();
facade.operation();
/* 输出:
Subsystem A operation
Subsystem B operation
*/

特性:为复杂子系统提供简化接口。

应用场景:简化复杂库或模块的调用。

§ 行为型模式

1. 观察者模式(Observer Pattern)

class Weather {
  constructor() {
    this.observers = [];
  }
 
  attach(observer) {
    this.observers.push(observer);
  }
 
  notify(temp) {
    this.observers.forEach(observer => observer.update(temp));
  }
 
  setTemp(temp) {
    this.notify(temp);
  }
}
 
class Observer {
  constructor(name) {
    this.name = name;
  }
 
  update(temp) {
    console.log(`${this.name} received temperature: ${temp}°C`);
  }
}
 
const weather = new Weather();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
 
weather.attach(observer1);
weather.attach(observer2);
weather.setTemp(25);
/* 输出:
Observer 1 received temperature: 25°C
Observer 2 received temperature: 25°C
*/

特性:定义一对多依赖关系,对象状态变化时通知所有观察者。

应用场景:事件处理、数据绑定。

2. 策略模式(Strategy Pattern)

class SortContext {
  constructor(strategy) {
    this.strategy = strategy;
  }
 
  setStrategy(strategy) {
    this.strategy = strategy;
  }
 
  execute(data) {
    return this.strategy.sort(data);
  }
}
 
class AscendingSort {
  sort(data) {
    return [...data].sort((a, b) => a - b);
  }
}
 
class DescendingSort {
  sort(data) {
    return [...data].sort((a, b) => b - a);
  }
}
 
const data = [3, 1, 4, 1, 5, 9, 2, 6];
const ascendingSort = new AscendingSort();
const descendingSort = new DescendingSort();
 
const context = new SortContext(ascendingSort);
console.log(context.execute(data)); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
 
context.setStrategy(descendingSort);
console.log(context.execute(data)); // 输出: [9, 6, 5, 4, 3, 2, 1, 1]

特性:封装一系列算法,客户端可动态切换。

应用场景:动态选择算法,如排序、表单验证规则。

3. 命令模式(Command Pattern)

class Light {
  turnOn() {
    console.log('Light is on');
  }
 
  turnOff() {
    console.log('Light is off');
  }
}
 
class Command {
  constructor(receiver) {
    this.receiver = receiver;
  }
 
  execute() {}
}
 
class TurnOnCommand extends Command {
  execute() {
    this.receiver.turnOn();
  }
}
 
class TurnOffCommand extends Command {
  execute() {
    this.receiver.turnOff();
  }
}
 
class RemoteControl {
  constructor() {
    this.command = null;
  }
 
  setCommand(command) {
    this.command = command;
  }
 
  pressButton() {
    this.command.execute();
  }
}
 
const light = new Light();
const turnOnCommand = new TurnOnCommand(light);
const turnOffCommand = new TurnOffCommand(light);
const remote = new RemoteControl();
 
remote.setCommand(turnOnCommand);
remote.pressButton(); // 输出: Light is on
 
remote.setCommand(turnOffCommand);
remote.pressButton(); // 输出: Light is off

特性:将请求封装为对象,支持撤销、重做或队列操作。

应用场景:实现撤销/重做功能、任务队列。

4. 状态模式(State Pattern)

class Order {
  constructor() {
    this.state = new PendingState(this);
  }
 
  setState(state) {
    this.state = state;
  }
 
  request() {
    this.state.handle();
  }
}
 
class PendingState {
  constructor(order) {
    this.order = order;
  }
 
  handle() {
    console.log('Order is pending');
    this.order.setState(new ProcessingState(this.order));
  }
}
 
class ProcessingState {
  constructor(order) {
    this.order = order;
  }
 
  handle() {
    console.log('Order is processing');
    this.order.setState(new ShippedState(this.order));
  }
}
 
class ShippedState {
  constructor(order) {
    this.order = order;
  }
 
  handle() {
    console.log('Order is shipped');
  }
}
 
const order = new Order();
order.request(); // 输出: Order is pending
order.request(); // 输出: Order is processing
order.request(); // 输出: Order is shipped

特性:允许对象根据内部状态改变行为。

应用场景:状态机,如订单状态管理、游戏角色状态切换。

§ 其他常见模式

1. 迭代器模式(Iterator Pattern)

class Iterator {
  constructor(data) {
    this.data = data;
    this.index = 0;
  }
 
  next() {
    if (this.index < this.data.length) {
      return { value: this.data[this.index++], done: false };
    } else {
      return { done: true };
    }
  }
}
 
const data = [1, 2, 3];
const iterator = new Iterator(data);
 
let result = iterator.next();
while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}
/* 输出:
1
2
3
*/

特性:提供统一接口遍历集合元素。

应用场景:遍历数组、链表等数据结构。

2. 发布-订阅模式(Pub/Sub Pattern)

class EventBus {
  constructor() {
    this.events = {};
  }
 
  subscribe(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
 
  publish(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
}
 
const eventBus = new EventBus();
 
eventBus.subscribe('event1', data => {
  console.log('Subscriber 1 received:', data);
});
 
eventBus.subscribe('event1', data => {
  console.log('Subscriber 2 received:', data);
});
 
eventBus.publish('event1', 'Hello World');
/* 输出:
Subscriber 1 received: Hello World
Subscriber 2 received: Hello World
*/

特性:解耦发布者与订阅者,支持一对多通信。

应用场景:事件总线、消息队列。

3. 模块模式(Module Pattern)

const CounterModule = (function() {
  let count = 0;
 
  function increment() {
    count++;
  }
 
  function getCount() {
    return count;
  }
 
  return {
    increment,
    getCount
  };
})();
 
CounterModule.increment();
console.log(CounterModule.getCount()); // 输出: 1
console.log(CounterModule.count); // 输出: undefined (私有变量不可访问)

特性:封装私有变量和方法,暴露公共接口。

应用场景:代码组织、避免全局污染。

§ 高阶函数

1. 什么是高阶函数?

高阶函数(Higher-Order Function)是指满足以下任一条件的函数:

1. 接受一个或多个函数作为参数

2. 返回一个函数作为结果

在编程中,高阶函数是将函数视为"一等公民"(first-class citizen)的语言特性体现,意味着函数可以像其他数据类型一样被传递和使用。

2. 高阶函数的特点

1. 函数作为参数:能够接收其他函数作为输入

2. 函数作为返回值:可以生成并返回新的函数

3. 抽象行为:能够抽象和封装常见的行为模式

4. 组合性:可以通过组合简单函数构建复杂功能

5. 延迟执行:返回的函数可以在需要时才执行

3. 高阶函数的用途

1. 抽象与代码复用

// 不使用高阶函数
const arr1 = [1, 2, 3].map(x => x * 2);
const arr2 = [1, 2, 3].map(x => x * 3);

// 使用高阶函数
function multiplyBy(factor) {
  return x => x * factor;
}
const arr1 = [1, 2, 3].map(multiplyBy(2));
const arr2 = [1, 2, 3].map(multiplyBy(3));

2. 回调机制

// 事件处理
button.addEventListener('click', () => {
  console.log('Button clicked!');
});

// 异步操作
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data));

3. 函数组合与管道

// 组合两个函数
const compose = (f, g) => x => f(g(x));
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;
const shout = compose(exclaim, toUpperCase);

console.log(shout('hello')); // 输出: "HELLO!"

4. 创建装饰器/中间件

// 日志装饰器
function withLogging(fn) {
  return (...args) => {
    console.log(`Calling with args: ${args}`);
    const result = fn(...args);
    console.log(`Result: ${result}`);
    return result;
  };
}

const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3); // 输出调用和结果信息

5. 柯里化与部分应用

// 柯里化函数
const curry = fn => {
  const arity = fn.length;
  return function $curry(...args) {
    if (args.length < arity) {
      return $curry.bind(null, ...args);
    }
    return fn.apply(null, args);
  };
};

const add = curry((a, b) => a + b);
const add2 = add(2);
console.log(add2(3)); // 5

4. 常见的高阶函数示例

数组方法mapfilterreducesort

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]

函数绑定bind

const greet = function(greeting, name) {
  return `${greeting}, ${name}!`;
};
const sayHello = greet.bind(null, 'Hello');
sayHello('Alice'); // "Hello, Alice!"

延迟执行

function lazyEvaluate(fn) {
  return function() {
    return fn.apply(this, arguments);
  };
}

5. 高阶函数的优势

1. 提高代码可读性:通过命名操作意图而非具体实现

2. 减少重复代码:抽象通用模式

3. 增强灵活性:行为可以通过参数动态配置

4. 支持函数式编程范式:如纯函数、不可变性等

5. 便于测试和维护:小的、可组合的函数单元

高阶函数是现代编程语言中强大的工具,尤其在JavaScript、Python、Haskell等语言中广泛应用,是函数式编程的核心概念之一。

§ 柯里化函数

1. 什么是柯里化函数?

柯里化(Currying)是一种将多参数函数转换为一系列单参数函数的技术。它得名于数学家Haskell Curry,核心思想是:一个接收多个参数的函数可以转换为接收单一参数的函数序列,每次调用返回一个新函数,直到所有参数都被提供,最终返回结果。

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

// 柯里化版本
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

// 使用方式
add(1, 2, 3); // 6
curriedAdd(1)(2)(3); // 6

2. 柯里化函数的特点

1. 参数分解:将多参数函数分解为单参数函数链

2. 延迟执行:在收集到足够参数前不会执行,返回新函数

3. 函数组合:便于创建可组合的函数管道

4. 闭包利用:每个返回的函数都闭包保存了之前传入的参数

5. 部分应用:可以提前固定部分参数,生成更专用的函数

3. 柯里化函数的特性

1. 参数记忆性

const add = a => b => a + b;
const add5 = add(5); // 记住第一个参数5
add5(3); // 8

2. 动态生成函数

const greet = greeting => name => `${greeting}, ${name}!`;
const sayHello = greet('Hello');
const sayHi = greet('Hi');

sayHello('Alice'); // "Hello, Alice!"
sayHi('Bob'); // "Hi, Bob!"

3. 自动柯里化

可以通过工具函数实现自动柯里化:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}

const curriedAdd = curry((a, b, c) => a + b + c);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6

4. 柯里化函数的用途

1. 参数复用

// 创建通用的URL构建器
const buildUrl = protocol => domain => path => `${protocol}://${domain}/${path}`;

const buildHttpsUrl = buildUrl('https');
const githubUrl = buildHttpsUrl('github.com');
githubUrl('username/repo'); // "https://github.com/username/repo"

2. 延迟执行/按需计算

// 日志函数
const logger = level => message => timestamp => 
  `[${timestamp}] [${level}] ${message}`;

const errorLogger = logger('ERROR');
const errorMessage = errorLogger('Database connection failed');
errorMessage(new Date().toISOString());

3. 函数组合

const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

const toUpper = str => str.toUpperCase();
const exclaim = str => `${str}!`;
const shout = compose(exclaim, toUpper);

shout('hello'); // "HELLO!"

4. 事件处理

// 事件监听器工厂
const createEventListener = eventType => element => handler => {
  element.addEventListener(eventType, handler);
  return () => element.removeEventListener(eventType, handler);
};

const createClickHandler = createEventListener('click');
const createButtonClick = createClickHandler(document.getElementById('myBtn'));
const unsubscribe = createButtonClick(e => console.log('Clicked!'));

5. 配置预设

// API请求构建器
const createRequest = baseUrl => headers => method => endpoint => data => {
  return fetch(`${baseUrl}/${endpoint}`, {
    method,
    headers,
    body: JSON.stringify(data)
  });
};

const apiRequest = createRequest('https://api.example.com')({ 'Content-Type': 'application/json' });
const getUsers = apiRequest('GET')('users');
const createUser = apiRequest('POST')('users');

5. 柯里化的优势

1. 代码复用:通过部分应用减少重复代码

2. 关注点分离:将参数获取与函数执行分离

3. 灵活性:动态生成具有预设参数的函数

4. 可读性:函数调用链更清晰地表达数据处理流程

5. 函数纯度:更容易保持函数纯净(无副作用)

6. 柯里化的局限性

1. 性能开销:嵌套函数调用可能带来轻微性能损失

2. 调试难度:调用栈可能变得更深更复杂

3. 学习曲线:对初学者可能不太直观

4. 过度使用:不是所有场景都适合柯里化

7. 实际应用场景

1. 函数式编程库:Lodash/fp、Ramda等

2. React高阶组件:参数化组件生成器

3. Redux中间件:如redux-thunk的action creators

4. Express中间件:配置预设的处理函数

5. 测试工具:创建特定的测试用例生成器