您现在的位置是:网站首页 > 代理模式(Proxy)的多种应用场景文章详情

代理模式(Proxy)的多种应用场景

代理模式是一种结构型设计模式,通过创建一个代理对象来控制对原始对象的访问。它在JavaScript中有广泛的应用场景,从性能优化到安全控制,再到功能增强,都能看到代理模式的身影。

远程代理:访问远程资源

远程代理是最经典的代理模式应用之一,它为远程对象提供本地代表。在前端开发中,最常见的场景是与后端API交互。

class ApiProxy {
  constructor(endpoint) {
    this.endpoint = endpoint;
  }

  async getData() {
    if (!this.cachedData) {
      const response = await fetch(this.endpoint);
      this.cachedData = await response.json();
    }
    return this.cachedData;
  }
}

// 使用代理
const apiProxy = new ApiProxy('https://api.example.com/data');
apiProxy.getData().then(data => console.log(data));

这个代理实现了缓存功能,避免重复请求相同的API。它还隐藏了网络请求的复杂性,让客户端代码更简洁。

虚拟代理:延迟初始化

虚拟代理用于延迟创建开销较大的对象,直到真正需要时才进行初始化。这在处理大型对象或资源密集型操作时特别有用。

class HeavyImage {
  constructor(src) {
    this.src = src;
    this.loadImage();
  }

  loadImage() {
    console.log(`Loading heavy image from ${this.src}`);
    // 实际加载图片的逻辑
  }

  display() {
    console.log(`Displaying image from ${this.src}`);
  }
}

class ImageProxy {
  constructor(src) {
    this.src = src;
    this.realImage = null;
  }

  display() {
    if (!this.realImage) {
      this.realImage = new HeavyImage(this.src);
    }
    this.realImage.display();
  }
}

// 使用代理
const imageProxy = new ImageProxy('large-image.jpg');
// 此时还没有真正加载图片
imageProxy.display(); // 第一次调用时才会加载

保护代理:访问控制

保护代理用于控制对原始对象的访问权限,常用于身份验证和授权场景。

class SensitiveData {
  constructor() {
    this.data = "Top Secret Information";
  }

  getData() {
    return this.data;
  }
}

class DataProxy {
  constructor(user) {
    this.realData = new SensitiveData();
    this.user = user;
  }

  getData() {
    if (this.user.isAdmin) {
      return this.realData.getData();
    } else {
      throw new Error("Unauthorized access");
    }
  }
}

// 使用代理
const admin = { isAdmin: true };
const user = { isAdmin: false };

const adminProxy = new DataProxy(admin);
console.log(adminProxy.getData()); // 可以访问

const userProxy = new DataProxy(user);
try {
  console.log(userProxy.getData()); // 抛出错误
} catch (e) {
  console.error(e.message);
}

缓存代理:优化性能

缓存代理可以存储昂贵操作的结果,避免重复计算,显著提高性能。

class ExpensiveCalculation {
  compute(input) {
    console.log(`Performing expensive calculation for ${input}`);
    // 模拟耗时计算
    return input * input;
  }
}

class CalculationProxy {
  constructor() {
    this.realCalculation = new ExpensiveCalculation();
    this.cache = new Map();
  }

  compute(input) {
    if (this.cache.has(input)) {
      console.log(`Returning cached result for ${input}`);
      return this.cache.get(input);
    }
    const result = this.realCalculation.compute(input);
    this.cache.set(input, result);
    return result;
  }
}

// 使用代理
const proxy = new CalculationProxy();
console.log(proxy.compute(5)); // 执行计算
console.log(proxy.compute(5)); // 返回缓存结果

ES6 Proxy:元编程能力

JavaScript内置的Proxy对象提供了更强大的元编程能力,可以拦截和自定义基本操作。

const user = {
  name: "John",
  age: 30,
  _password: "secret"
};

const userProxy = new Proxy(user, {
  get(target, prop) {
    if (prop.startsWith('_')) {
      throw new Error("Access denied");
    }
    return target[prop];
  },
  set(target, prop, value) {
    if (prop.startsWith('_')) {
      throw new Error("Access denied");
    }
    target[prop] = value;
    return true;
  },
  ownKeys(target) {
    return Object.keys(target).filter(key => !key.startsWith('_'));
  }
});

console.log(userProxy.name); // "John"
try {
  console.log(userProxy._password); // 抛出错误
} catch (e) {
  console.error(e.message);
}

console.log(Object.keys(userProxy)); // ["name", "age"]

日志代理:调试和监控

代理模式可以方便地添加日志功能,用于调试或监控对象的使用情况。

class Calculator {
  add(a, b) {
    return a + b;
  }

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

function createLoggingProxy(target) {
  return new Proxy(target, {
    get(target, prop) {
      if (typeof target[prop] === 'function') {
        return function(...args) {
          console.log(`Calling ${prop} with args: ${args}`);
          const result = target[prop](...args);
          console.log(`Result: ${result}`);
          return result;
        };
      }
      return target[prop];
    }
  });
}

const calculator = new Calculator();
const loggingCalculator = createLoggingProxy(calculator);

loggingCalculator.add(2, 3);
// 输出:
// Calling add with args: 2,3
// Result: 5

验证代理:输入校验

代理可以在方法调用前进行参数验证,确保输入符合要求。

class UserService {
  createUser(name, age) {
    return { id: Date.now(), name, age };
  }
}

function createValidationProxy(target) {
  return new Proxy(target, {
    get(target, prop) {
      if (typeof target[prop] === 'function') {
        return function(...args) {
          const [name, age] = args;
          if (typeof name !== 'string' || name.length < 2) {
            throw new Error("Name must be at least 2 characters long");
          }
          if (typeof age !== 'number' || age < 0 || age > 120) {
            throw new Error("Age must be between 0 and 120");
          }
          return target[prop](...args);
        };
      }
      return target[prop];
    }
  });
}

const userService = new UserService();
const validatedUserService = createValidationProxy(userService);

try {
  validatedUserService.createUser("J", 25); // 抛出错误
} catch (e) {
  console.error(e.message);
}

const user = validatedUserService.createUser("John", 30);
console.log(user); // 正常创建用户

组合代理:多重功能

实际应用中,我们经常需要组合多种代理功能,创建一个具有多种特性的代理对象。

class BankAccount {
  constructor(balance = 0) {
    this._balance = balance;
  }

  deposit(amount) {
    this._balance += amount;
    return this._balance;
  }

  withdraw(amount) {
    if (amount > this._balance) {
      throw new Error("Insufficient funds");
    }
    this._balance -= amount;
    return this._balance;
  }

  get balance() {
    return this._balance;
  }
}

function createEnhancedProxy(target) {
  return new Proxy(target, {
    get(target, prop) {
      // 保护私有属性
      if (prop.startsWith('_')) {
        throw new Error("Access denied");
      }
      
      const value = target[prop];
      
      // 如果是方法,添加日志和验证
      if (typeof value === 'function') {
        return function(...args) {
          console.log(`Calling ${prop} with args: ${args}`);
          
          // 验证存款/取款金额
          if (['deposit', 'withdraw'].includes(prop)) {
            const [amount] = args;
            if (typeof amount !== 'number' || amount <= 0) {
              throw new Error("Amount must be a positive number");
            }
          }
          
          const result = value.apply(target, args);
          console.log(`Result: ${result}`);
          return result;
        };
      }
      
      return value;
    }
  });
}

const account = new BankAccount(100);
const secureAccount = createEnhancedProxy(account);

secureAccount.deposit(50); // 正常
try {
  secureAccount.withdraw(-10); // 抛出错误
} catch (e) {
  console.error(e.message);
}

try {
  console.log(secureAccount._balance); // 抛出错误
} catch (e) {
  console.error(e.message);
}

性能监控代理

代理模式可以用于监控方法的执行时间,帮助识别性能瓶颈。

class DataProcessor {
  processLargeData(data) {
    // 模拟耗时操作
    for (let i = 0; i < 1000000000; i++) {}
    return data.map(item => item * 2);
  }
}

function createPerformanceProxy(target) {
  return new Proxy(target, {
    get(target, prop) {
      const originalMethod = target[prop];
      if (typeof originalMethod === 'function') {
        return function(...args) {
          const start = performance.now();
          const result = originalMethod.apply(target, args);
          const end = performance.now();
          console.log(`${prop} executed in ${end - start} milliseconds`);
          return result;
        };
      }
      return originalMethod;
    }
  });
}

const processor = new DataProcessor();
const monitoredProcessor = createPerformanceProxy(processor);

const data = Array.from({ length: 100 }, (_, i) => i);
monitoredProcessor.processLargeData(data);
// 输出: processLargeData executed in X milliseconds

惰性加载代理

对于需要按需加载的资源,代理模式可以实现惰性加载策略。

class VideoPlayer {
  constructor(videoId) {
    this.videoId = videoId;
    this.loadPlayer();
  }

  loadPlayer() {
    console.log(`Loading heavy video player for video ${this.videoId}`);
    // 实际加载视频播放器的逻辑
  }

  play() {
    console.log(`Playing video ${this.videoId}`);
  }
}

class LazyVideoPlayer {
  constructor(videoId) {
    this.videoId = videoId;
    this.realPlayer = null;
  }

  play() {
    if (!this.realPlayer) {
      this.realPlayer = new VideoPlayer(this.videoId);
    }
    this.realPlayer.play();
  }
}

// 使用代理
const lazyPlayer = new LazyVideoPlayer('video123');
// 此时还没有加载真正的播放器
lazyPlayer.play(); // 第一次调用时才会加载

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步